diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5abedea..a584147 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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)
diff --git a/NMEA0183/MWVSentence.cs b/NMEA0183/MWVSentence.cs
new file mode 100644
index 0000000..09094e5
--- /dev/null
+++ b/NMEA0183/MWVSentence.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TensionDev.Maritime.NMEA0183
+{
+ ///
+ /// MWV - Wind Speed and Angle
+ ///
+ public class MWVSentence : NMEASentence
+ {
+ ///
+ /// Wind angle, 0 to 359 degrees
+ ///
+ public Decimal WindAngleDegrees { get; set; }
+
+ ///
+ /// Reference, Relative or Theoretical
+ ///
+ public WindReferenceEnum WindReference { get; set; }
+
+ ///
+ /// Wind speed
+ ///
+ public Decimal WindSpeed { get; set; }
+
+ ///
+ /// Wind speed units, K/M/N
+ ///
+ public WindSpeedUnitsEnum WindSpeedUnits { get; set; }
+
+ ///
+ /// Is the data valid?
+ ///
+ 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
+ {
+ ///
+ /// Relative
+ ///
+ R,
+ ///
+ /// Theoretical
+ ///
+ T,
+ }
+
+ public enum WindSpeedUnitsEnum
+ {
+ ///
+ /// km/h - Kilometres per Hour
+ ///
+ K,
+ ///
+ /// m/s - Metres per Second
+ ///
+ M,
+ ///
+ /// Knots - Nautical Mile per Hour
+ ///
+ N,
+ }
+ }
+}
diff --git a/NMEA0183/NMEA0183.csproj b/NMEA0183/NMEA0183.csproj
index 94c1565..f1c5eaa 100644
--- a/NMEA0183/NMEA0183.csproj
+++ b/NMEA0183/NMEA0183.csproj
@@ -7,7 +7,7 @@
true
true
TensionDev.Maritime.NMEA0183
- 0.1.0-alpha
+ 0.2.0-alpha
TensionDev amsga
TensionDev
TensionDev.Maritime.NMEA0183
@@ -20,8 +20,8 @@
NMEA0183
Initial project release
en-SG
- 0.1.0.0
- 0.1.0.0
+ 0.2.0.0
+ 0.2.0.0
true
snupkg
diff --git a/NMEA0183/NMEASentence.cs b/NMEA0183/NMEASentence.cs
index e3f3e76..bc3da9c 100644
--- a/NMEA0183/NMEASentence.cs
+++ b/NMEA0183/NMEASentence.cs
@@ -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.");
}
diff --git a/NMEA0183/QuerySentence.cs b/NMEA0183/QuerySentence.cs
new file mode 100644
index 0000000..e94a4e8
--- /dev/null
+++ b/NMEA0183/QuerySentence.cs
@@ -0,0 +1,53 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace TensionDev.Maritime.NMEA0183
+{
+ ///
+ /// Q - Query Sentence
+ ///
+ public class QuerySentence : NMEASentence
+ {
+ ///
+ /// Listener Identifier
+ ///
+ public TalkerIdentifier ListenerIdentifier { get; set; }
+
+ ///
+ /// Sentence Identifier
+ ///
+ 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];
+ }
+ }
+}
diff --git a/NMEA0183/ZDASentence.cs b/NMEA0183/ZDASentence.cs
new file mode 100644
index 0000000..029b993
--- /dev/null
+++ b/NMEA0183/ZDASentence.cs
@@ -0,0 +1,79 @@
+using System;
+using System.Text;
+
+namespace TensionDev.Maritime.NMEA0183
+{
+ ///
+ /// ZDA - Time & Date
+ ///
+ public class ZDASentence : NMEASentence
+ {
+ ///
+ /// UTC Date and Time At Position
+ ///
+ public DateTime UTCDateTimeAtPosition { get; set; }
+
+ ///
+ /// Local Time Zone
+ ///
+ 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());
+ }
+ }
+}
diff --git a/XUnitTestProjectNMEA0183/UnitTestMWVSentence.cs b/XUnitTestProjectNMEA0183/UnitTestMWVSentence.cs
new file mode 100644
index 0000000..6c796c9
--- /dev/null
+++ b/XUnitTestProjectNMEA0183/UnitTestMWVSentence.cs
@@ -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);
+ }
+ }
+}
diff --git a/XUnitTestProjectNMEA0183/UnitTestQuerySentence.cs b/XUnitTestProjectNMEA0183/UnitTestQuerySentence.cs
new file mode 100644
index 0000000..6ce37c1
--- /dev/null
+++ b/XUnitTestProjectNMEA0183/UnitTestQuerySentence.cs
@@ -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);
+ }
+ }
+}
diff --git a/XUnitTestProjectNMEA0183/UnitTestZDASentence.cs b/XUnitTestProjectNMEA0183/UnitTestZDASentence.cs
new file mode 100644
index 0000000..1cfecb4
--- /dev/null
+++ b/XUnitTestProjectNMEA0183/UnitTestZDASentence.cs
@@ -0,0 +1,26 @@
+using System;
+using TensionDev.Maritime.NMEA0183;
+using Xunit;
+
+namespace XUnitTestProjectNMEA0183
+{
+ public class UnitTestZDASentence
+ {
+ [Fact]
+ public void ZDADecoding()
+ {
+ String sentence = "$GPZDA,160012.71,11,03,2004,-1,00*7D";
+ DateTime dateTimeUTC = new DateTime(2004, 03, 11, 16, 00, 12, 710, DateTimeKind.Utc);
+ TimeSpan timeZone = new TimeSpan(1, 0, 0);
+
+ NMEASentence nmeaSentence = NMEASentence.DecodeSentence(sentence);
+ ZDASentence zdaSentence = nmeaSentence as ZDASentence;
+
+ Assert.NotNull(nmeaSentence);
+ Assert.NotNull(zdaSentence);
+ Assert.Equal(TalkerIdentifierEnum.GlobalPositioningSystem, zdaSentence.TalkerIdentifier.TalkerIdentifierEnum);
+ Assert.Equal(dateTimeUTC, zdaSentence.UTCDateTimeAtPosition);
+ Assert.Equal(timeZone, zdaSentence.LocalTimeZone);
+ }
+ }
+}