Skip to content

Commit

Permalink
Additional NMEA 0183 sentences
Browse files Browse the repository at this point in the history
Created the QuerySentence, to represent query sentences.
Created the ZDASentence, to represent Time & Date sentences.
Created the MWVSentence, to represent Wind Speed and Angle sentences.
  • Loading branch information
amsga committed Sep 11, 2021
1 parent b8f8c0a commit 0be6c62
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 3 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Added DTM Sentence.
- Added GST Sentence.
- Added THS Sentence.
- Added TTM Sentence.
- Added VBW Sentence.
- Added VTG Sentence.

## [v0.2.0-alpha] - 2021-09-11
[v0.2.0-alpha](https://github.com/TensionDev/NMEA0183/releases/tag/v0.2.0-alpha)

### Changed
- Changed Namespace from TensionDev.NMEA0183 to TensionDev.Maritime.NMEA0183 to reflect possible expansion to other maritime implementations.

### Added
- Added Query Sentence.
- Added MWV Sentence.
- Added ZDA Sentence.


## [v0.1.0-alpha] - 2021-09-10
[v0.1.0-alpha](https://github.com/TensionDev/NMEA0183/releases/tag/v0.1.0-alpha)
Expand Down
123 changes: 123 additions & 0 deletions NMEA0183/MWVSentence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace TensionDev.Maritime.NMEA0183
{
/// <summary>
/// MWV - Wind Speed and Angle
/// </summary>
public class MWVSentence : NMEASentence
{
/// <summary>
/// Wind angle, 0 to 359 degrees
/// </summary>
public Decimal WindAngleDegrees { get; set; }

/// <summary>
/// Reference, Relative or Theoretical
/// </summary>
public WindReferenceEnum WindReference { get; set; }

/// <summary>
/// Wind speed
/// </summary>
public Decimal WindSpeed { get; set; }

/// <summary>
/// Wind speed units, K/M/N
/// </summary>
public WindSpeedUnitsEnum WindSpeedUnits { get; set; }

/// <summary>
/// Is the data valid?
/// </summary>
public Boolean IsDataValid { get; set; }

public MWVSentence()
{
SentenceIdentifier = "MWV";
WindReference = WindReferenceEnum.R;
WindSpeedUnits = WindSpeedUnitsEnum.N;
IsDataValid = false;
}

public override String EncodeSentence()
{
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.AppendFormat("${0}{1},", TalkerIdentifier.ToString(), SentenceIdentifier);

stringBuilder.AppendFormat("{0},", WindAngleDegrees);

stringBuilder.AppendFormat("{0},", WindReference.ToString());

stringBuilder.AppendFormat("{0},", WindSpeed);

stringBuilder.AppendFormat("{0},", WindSpeedUnits.ToString());

if (IsDataValid)
stringBuilder.Append("A");
else
stringBuilder.Append("V");

Byte checksum = CalculateChecksum(stringBuilder.ToString());

stringBuilder.AppendFormat("*{0}\r\n", checksum.ToString("X2"));

return stringBuilder.ToString();
}

protected override void DecodeInternalSentence(String sentence)
{
DecodeTalker(sentence);

String[] vs = sentence.Split(new char[] { ',', '*' });

// Wind angle
WindAngleDegrees = Decimal.Parse(vs[1]);

// Wind reference
WindReference = (WindReferenceEnum)Enum.Parse(typeof(WindReferenceEnum), vs[2]);

// Wind speed
WindSpeed = Decimal.Parse(vs[3]);

// Wind speed units
WindSpeedUnits = (WindSpeedUnitsEnum)Enum.Parse(typeof(WindSpeedUnitsEnum), vs[4]);

if (vs[5] == "A")
IsDataValid = true;
else
IsDataValid = false;
}

public enum WindReferenceEnum
{
/// <summary>
/// Relative
/// </summary>
R,
/// <summary>
/// Theoretical
/// </summary>
T,
}

public enum WindSpeedUnitsEnum
{
/// <summary>
/// km/h - Kilometres per Hour
/// </summary>
K,
/// <summary>
/// m/s - Metres per Second
/// </summary>
M,
/// <summary>
/// Knots - Nautical Mile per Hour
/// </summary>
N,
}
}
}
6 changes: 3 additions & 3 deletions NMEA0183/NMEA0183.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageId>TensionDev.Maritime.NMEA0183</PackageId>
<Version>0.1.0-alpha</Version>
<Version>0.2.0-alpha</Version>
<Authors>TensionDev amsga</Authors>
<Company>TensionDev</Company>
<Product>TensionDev.Maritime.NMEA0183</Product>
Expand All @@ -20,8 +20,8 @@
<PackageTags>NMEA0183</PackageTags>
<PackageReleaseNotes>Initial project release</PackageReleaseNotes>
<NeutralLanguage>en-SG</NeutralLanguage>
<AssemblyVersion>0.1.0.0</AssemblyVersion>
<FileVersion>0.1.0.0</FileVersion>
<AssemblyVersion>0.2.0.0</AssemblyVersion>
<FileVersion>0.2.0.0</FileVersion>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
Expand Down
8 changes: 8 additions & 0 deletions NMEA0183/NMEASentence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ public static NMEASentence DecodeSentence(String sentence)
nmeaSentence = new HDTSentence();
break;

case "MWV":
nmeaSentence = new MWVSentence();
break;

case "ZDA":
nmeaSentence = new ZDASentence();
break;

default:
throw new NotImplementedException("Sentence Identifier not recognised.");
}
Expand Down
53 changes: 53 additions & 0 deletions NMEA0183/QuerySentence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace TensionDev.Maritime.NMEA0183
{
/// <summary>
/// Q - Query Sentence
/// </summary>
public class QuerySentence : NMEASentence
{
/// <summary>
/// Listener Identifier
/// </summary>
public TalkerIdentifier ListenerIdentifier { get; set; }

/// <summary>
/// Sentence Identifier
/// </summary>
public String QuerySentenceIdentifier { get; set; }

public QuerySentence()
{
SentenceIdentifier = "Q";
}

public override String EncodeSentence()
{
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.AppendFormat("${0}{1}{2},", TalkerIdentifier.ToString(), ListenerIdentifier.ToString(), SentenceIdentifier);

stringBuilder.AppendFormat("{0}", QuerySentenceIdentifier);

Byte checksum = CalculateChecksum(stringBuilder.ToString());

stringBuilder.AppendFormat("*{0}\r\n", checksum.ToString("X2"));

return stringBuilder.ToString();
}

protected override void DecodeInternalSentence(String sentence)
{
DecodeTalker(sentence);

String[] vs = sentence.Split(new char[] { ',', '*' });

ListenerIdentifier = TalkerIdentifier.FromString(vs[0].Substring(3, 2));

QuerySentenceIdentifier = vs[1];
}
}
}
79 changes: 79 additions & 0 deletions NMEA0183/ZDASentence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Text;

namespace TensionDev.Maritime.NMEA0183
{
/// <summary>
/// ZDA - Time & Date
/// </summary>
public class ZDASentence : NMEASentence
{
/// <summary>
/// UTC Date and Time At Position
/// </summary>
public DateTime UTCDateTimeAtPosition { get; set; }

/// <summary>
/// Local Time Zone
/// </summary>
public TimeSpan LocalTimeZone { get; set; }

protected TimeSpan DifferenceToUtc { get { return TimeSpan.Zero - LocalTimeZone; } set { LocalTimeZone = TimeSpan.Zero - value; } }

public ZDASentence()
{
SentenceIdentifier = "ZDA";
UTCDateTimeAtPosition = DateTime.UtcNow;
LocalTimeZone = TimeZoneInfo.Local.BaseUtcOffset;
}

public override String EncodeSentence()
{
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.AppendFormat("${0}{1},", TalkerIdentifier.ToString(), SentenceIdentifier);

stringBuilder.AppendFormat("{0},", UTCDateTimeAtPosition.ToString("HHmmss.FF"));

stringBuilder.AppendFormat("{0},", UTCDateTimeAtPosition.ToString("dd"));

stringBuilder.AppendFormat("{0},", UTCDateTimeAtPosition.ToString("MM"));

stringBuilder.AppendFormat("{0},", UTCDateTimeAtPosition.ToString("yyyy"));

if (DifferenceToUtc < TimeSpan.Zero)
stringBuilder.Append("-");
stringBuilder.AppendFormat("{0},{1}", DifferenceToUtc.ToString("hh"), DifferenceToUtc.ToString("mm"));

Byte checksum = CalculateChecksum(stringBuilder.ToString());

stringBuilder.AppendFormat("*{0}\r\n", checksum.ToString("X2"));

return stringBuilder.ToString();
}

protected override void DecodeInternalSentence(String sentence)
{
DecodeTalker(sentence);

String[] vs = sentence.Split(new char[] { ',', '*' });

// UTC Time
String time = vs[1];
time = time.Insert(4, ":");
time = time.Insert(2, ":");
TimeSpan timeSpan = TimeSpan.Parse(time);

// UTC Date
Int32 day = Int32.Parse(vs[2]);
Int32 month = Int32.Parse(vs[3]);
Int32 year = Int32.Parse(vs[4]);

UTCDateTimeAtPosition = new DateTime(year, month, day, timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds, timeSpan.Milliseconds, DateTimeKind.Utc);

StringBuilder UTCOffset = new StringBuilder();
UTCOffset.AppendFormat("{0}:{1}", vs[5], vs[6]);
DifferenceToUtc = TimeSpan.Parse(UTCOffset.ToString());
}
}
}
27 changes: 27 additions & 0 deletions XUnitTestProjectNMEA0183/UnitTestMWVSentence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using TensionDev.Maritime.NMEA0183;
using Xunit;

namespace XUnitTestProjectNMEA0183
{
public class UnitTestMWVSentence
{
[Fact]
public void MWVDecoding()
{
String sentence = "$WIMWV,214.8,R,0.1,K,A*28";

NMEASentence nmeaSentence = NMEASentence.DecodeSentence(sentence);
MWVSentence mwvSentence = nmeaSentence as MWVSentence;

Assert.NotNull(nmeaSentence);
Assert.NotNull(mwvSentence);
Assert.Equal(TalkerIdentifierEnum.WeatherInstruments, mwvSentence.TalkerIdentifier.TalkerIdentifierEnum);
Assert.Equal(214.8M, mwvSentence.WindAngleDegrees);
Assert.Equal(MWVSentence.WindReferenceEnum.R, mwvSentence.WindReference);
Assert.Equal(0.1M, mwvSentence.WindSpeed);
Assert.Equal(MWVSentence.WindSpeedUnitsEnum.K, mwvSentence.WindSpeedUnits);
Assert.True(mwvSentence.IsDataValid);
}
}
}
26 changes: 26 additions & 0 deletions XUnitTestProjectNMEA0183/UnitTestQuerySentence.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using TensionDev.Maritime.NMEA0183;
using Xunit;

namespace XUnitTestProjectNMEA0183
{
public class UnitTestQuerySentence
{
[Fact]
public void QueryEncoding()
{
String expectedSentence = "$ECGPQ,GGA*2D\r\n";

QuerySentence querySentence = new QuerySentence()
{
TalkerIdentifier = new TalkerIdentifier() { TalkerIdentifierEnum = TalkerIdentifierEnum.ElectronicChartDisplayInformationSystem },
ListenerIdentifier = new TalkerIdentifier() { TalkerIdentifierEnum = TalkerIdentifierEnum.GlobalPositioningSystem },
QuerySentenceIdentifier = "GGA"
};

String sentence = querySentence.EncodeSentence();

Assert.Equal(expectedSentence, sentence);
}
}
}
Loading

0 comments on commit 0be6c62

Please sign in to comment.