Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding UnixTimestamp + Parse message type 27 #149

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Ais.Net.Models.Abstractions
{
public interface IAisMessage : IVesselIdentity, IAisMessageType
public interface IAisMessage : IVesselIdentity, IAisMessageType, ITimestamp
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Ais.Net.Models.Abstractions
{
public interface IAisMessageType27
{
float? CourseOverGroundDegrees { get; }

bool NotGnssPosition { get; }

NavigationStatus NavigationStatus { get; }

Position? Position { get; }

bool PositionAccuracy { get; }

float? SpeedOverGround { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Ais.Net.Models.Abstractions;

public interface ITimestamp
{
public long? UnixTimestamp { get; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unix timestamps come in two different units: seconds and milliseconds. I don't know which this is. Please could you rename it to make the units clear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was simply using the field name that you had used to maintain continuity but can rename if desired?

https://github.com/ais-dotnet/Ais.Net/blob/3f0624713cd879f0234b167e9e77b899765e8843/Solutions/Ais.Net/Ais/Net/NmeaTagBlockParser.cs#L141

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ public record Position(double Latitude, double Longitude)
{
public static Position From10000thMins(int latitude, int longitude) =>
new (latitude.From10000thMinsToDegrees(), longitude.From10000thMinsToDegrees());
public static Position From10thMins(int latitude, int longitude) =>
new (latitude.From10thMinsToDegrees(), longitude.From10thMinsToDegrees());
}
}
2 changes: 1 addition & 1 deletion Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ namespace Ais.Net.Models
{
using Ais.Net.Models.Abstractions;

public record AisMessageBase(int MessageType, uint Mmsi) : IAisMessage;
public record AisMessageBase(int MessageType, uint Mmsi, long? UnixTimestamp) : IAisMessage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ public static double From10000thMinsToDegrees(this int value)
{
return value / 600000.0;
}

public static double From10thMinsToDegrees(this int value)
{
return value / 600.0;
}

public static float? FromTenths(this uint value)
{
Expand Down
5 changes: 3 additions & 2 deletions Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType18.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ public record AisMessageType18(
uint RepeatIndicator,
float? SpeedOverGround,
uint TimeStampSecond,
uint TrueHeadingDegrees) :
AisMessageBase(MessageType: 18, Mmsi),
uint TrueHeadingDegrees,
long? UnixTimestamp) :
AisMessageBase(MessageType: 18, Mmsi, UnixTimestamp),
IAisMessageType18,
IAisIsAssigned,
IRaimFlag,
Expand Down
5 changes: 3 additions & 2 deletions Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType19.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public record AisMessageType19(
uint Spare308,
float? SpeedOverGround,
uint TimeStampSecond,
uint TrueHeadingDegrees) :
AisMessageBase(MessageType: 19, Mmsi),
uint TrueHeadingDegrees,
long? UnixTimestamp) :
AisMessageBase(MessageType: 19, Mmsi, UnixTimestamp),
IAisMessageType19,
IAisIsAssigned,
IAisIsDteNotReady,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ public record AisMessageType1Through3(
uint SpareBits145,
float? SpeedOverGround,
uint TimeStampSecond,
uint TrueHeadingDegrees) :
AisMessageBase(MessageType, Mmsi),
uint TrueHeadingDegrees,
long? UnixTimestamp) :
AisMessageBase(MessageType, Mmsi, UnixTimestamp),
IAisMessageType1to3,
IRaimFlag,
IRepeatIndicator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ public record AisMessageType24Part0(
uint Mmsi,
uint PartNumber,
uint RepeatIndicator,
uint Spare160) :
AisMessageBase(MessageType: 24, Mmsi),
uint Spare160,
long? UnixTimestamp) :
AisMessageBase(MessageType: 24, Mmsi, UnixTimestamp),
IAisMultipartMessage,
IRepeatIndicator,
IAisMessageType24Part0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ public record AisMessageType24Part1(
ShipType ShipType,
uint UnitModelCode,
string VendorIdRev3,
string VendorIdRev4) :
AisMessageBase(MessageType: 24, Mmsi),
string VendorIdRev4,
long? UnixTimestamp) :
AisMessageBase(MessageType: 24, Mmsi, UnixTimestamp),
IAisMessageType24Part1,
IAisMultipartMessage,
ICallSign,
Expand Down
24 changes: 24 additions & 0 deletions Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType27.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// <copyright file="AisMessageType18.cs" company="Endjin Limited">
// Copyright (c) Endjin Limited. All rights reserved.
// </copyright>

namespace Ais.Net.Models
{
using Ais.Net.Models.Abstractions;

public record AisMessageType27(
float? CourseOverGroundDegrees,
bool NotGnssPosition,
uint Mmsi,
NavigationStatus NavigationStatus,
Position? Position,
bool PositionAccuracy,
bool RaimFlag,
uint RepeatIndicator,
float? SpeedOverGround,
long? UnixTimestamp) :
AisMessageBase(MessageType: 27, Mmsi, UnixTimestamp),
IAisMessageType27,
IRaimFlag,
IRepeatIndicator;
}
5 changes: 3 additions & 2 deletions Solutions/Ais.Net.Models/Ais/Net/Models/AisMessageType5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ public record AisMessageType5(
uint RepeatIndicator,
ShipType ShipType,
uint Spare423,
string VesselName) :
AisMessageBase(MessageType: 5, Mmsi),
string VesselName,
long? UnixTimestamp) :
AisMessageBase(MessageType: 5, Mmsi, UnixTimestamp),
IAisMessageType5,
IAisIsDteNotReady,
IAisPositionFixType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,31 +30,37 @@ public void OnNext(in NmeaLineParser parsedLine, in ReadOnlySpan<byte> asciiPayl
{
case >= 1 and <= 3:
{
this.ParseMessageTypes1Through3(asciiPayload, padding, messageType);
this.ParseMessageTypes1Through3(parsedLine, asciiPayload, padding, messageType);
return;
}

case 5:
{
this.ParseMessageType5(asciiPayload, padding);
this.ParseMessageType5(parsedLine, asciiPayload, padding);
return;
}

case 18:
{
this.ParseMessageType18(asciiPayload, padding);
this.ParseMessageType18(parsedLine, asciiPayload, padding);
return;
}

case 19:
{
this.ParseMessageType19(asciiPayload, padding);
this.ParseMessageType19(parsedLine, asciiPayload, padding);
return;
}

case 24:
{
this.ParseMessageType24(asciiPayload, padding);
this.ParseMessageType24(parsedLine, asciiPayload, padding);
return;
}

case 27:
{
this.ParseMessageType27(parsedLine, asciiPayload, padding);
return;
}
}
Expand Down Expand Up @@ -87,7 +93,7 @@ public void Progress(
throw new NotImplementedException();
}

private void ParseMessageTypes1Through3(ReadOnlySpan<byte> asciiPayload, uint padding, int messageType)
private void ParseMessageTypes1Through3(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding, int messageType)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that we're now passing in a line parser and the ASCII payload seemed like a code smell to me, and now has me wondering: is there a layering issue here?

The timestamp isn't really part of the AIS message, it's actually from the surrounding NMEA layer, isn't it?

Before this change, these various message types represent the information that was broadcast by the vessels' AIS systems. The timestamp is not part of that. The provenance of the timestamp is not necessarily clear. It might tell us when the ground station that picked up the radio transmission received it. But in cases where messages are relayed as part of a large-scale network that collects AIS data from numerous stations, it might just tell you when the particular equipment you're plugged into received the message.

So for that reason I'm not sure if it is really correct to attach this to the types that represent AIS messages.

If you need access to information from the NMEA layer, might it be better to design in something where we make that available, and make it clear which pieces of information came from where?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again my scope to refactor and improve general code quality is limited as I have many other commitments but would welcome PRs on my repository if you dont want to undertake this work in yours.

{
var parser = new NmeaAisPositionReportClassAParser(asciiPayload, padding);

Expand All @@ -108,12 +114,13 @@ private void ParseMessageTypes1Through3(ReadOnlySpan<byte> asciiPayload, uint pa
SpareBits145: parser.SpareBits145,
SpeedOverGround: parser.SpeedOverGroundTenths.FromTenths(),
TimeStampSecond: parser.TimeStampSecond,
TrueHeadingDegrees: parser.TrueHeadingDegrees);
TrueHeadingDegrees: parser.TrueHeadingDegrees,
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
}

private void ParseMessageType5(ReadOnlySpan<byte> asciiPayload, uint padding)
private void ParseMessageType5(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding)
{
var parser = new NmeaAisStaticAndVoyageRelatedDataParser(asciiPayload, padding);

Expand All @@ -137,12 +144,13 @@ private void ParseMessageType5(ReadOnlySpan<byte> asciiPayload, uint padding)
DimensionToStern: parser.DimensionToStern,
Draught10thMetres: parser.Draught10thMetres,
Spare423: parser.Spare423,
PositionFixType: parser.PositionFixType);
PositionFixType: parser.PositionFixType,
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
}

private void ParseMessageType18(ReadOnlySpan<byte> asciiPayload, uint padding)
private void ParseMessageType18(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding)
{
var parser = new NmeaAisPositionReportClassBParser(asciiPayload, padding);

Expand All @@ -164,12 +172,13 @@ private void ParseMessageType18(ReadOnlySpan<byte> asciiPayload, uint padding)
TrueHeadingDegrees: parser.TrueHeadingDegrees,
IsAssigned: parser.IsAssigned,
RaimFlag: parser.RaimFlag,
RepeatIndicator: parser.RepeatIndicator);
RepeatIndicator: parser.RepeatIndicator,
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
}

private void ParseMessageType19(ReadOnlySpan<byte> asciiPayload, uint padding)
private void ParseMessageType19(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding)
{
var parser = new NmeaAisPositionReportExtendedClassBParser(asciiPayload, padding);

Expand Down Expand Up @@ -197,12 +206,13 @@ private void ParseMessageType19(ReadOnlySpan<byte> asciiPayload, uint padding)
SpeedOverGround: parser.SpeedOverGroundTenths.FromTenths(),
TimeStampSecond: parser.TimeStampSecond,
TrueHeadingDegrees: parser.TrueHeadingDegrees,
Position: Position.From10000thMins(parser.Latitude10000thMins, parser.Longitude10000thMins));
Position: Position.From10000thMins(parser.Latitude10000thMins, parser.Longitude10000thMins),
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
}

private void ParseMessageType24(ReadOnlySpan<byte> asciiPayload, uint padding)
private void ParseMessageType24(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding)
{
uint part = NmeaAisStaticDataReportParser.GetPartNumber(asciiPayload, padding);

Expand All @@ -219,7 +229,8 @@ private void ParseMessageType24(ReadOnlySpan<byte> asciiPayload, uint padding)
Mmsi: parser.Mmsi,
PartNumber: parser.PartNumber,
RepeatIndicator: parser.RepeatIndicator,
Spare160: parser.Spare160);
Spare160: parser.Spare160,
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
break;
Expand Down Expand Up @@ -253,12 +264,32 @@ private void ParseMessageType24(ReadOnlySpan<byte> asciiPayload, uint padding)
Spare162: parser.Spare162,
UnitModelCode: parser.UnitModelCode,
VendorIdRev3: vendorIdRev3Ascii.GetString(),
VendorIdRev4: vendorIdRev4Ascii.GetString());
VendorIdRev4: vendorIdRev4Ascii.GetString(),
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
break;
}
}
}

private void ParseMessageType27(NmeaLineParser nmeaLineParser, ReadOnlySpan<byte> asciiPayload, uint padding)
{
var parser = new NmeaAisLongRangeAisBroadcastParser(asciiPayload, padding);

var message = new AisMessageType27(
Mmsi: parser.Mmsi,
Position: Position.From10thMins(parser.Latitude10thMins, parser.Longitude10thMins),
CourseOverGroundDegrees: parser.CourseOverGroundDegrees,
PositionAccuracy: parser.PositionAccuracy,
SpeedOverGround: parser.SpeedOverGroundTenths.FromTenths(),
RaimFlag: parser.RaimFlag,
RepeatIndicator: parser.RepeatIndicator,
NotGnssPosition: parser.NotGnssPosition,
NavigationStatus: parser.NavigationStatus,
UnixTimestamp: nmeaLineParser.TagBlock.UnixTimestamp);

this.messages.OnNext(message);
}
}
}