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

Add support for a few new messages, fix warnings from AisManager #2158

Merged
merged 10 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 9 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
98 changes: 98 additions & 0 deletions src/Iot.Device.Bindings/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,20 @@
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Iot.Device.Nmea0183.AisManager.CleanupLatency</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Iot.Device.Nmea0183.AisManager.WarningRepeatTimeout</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Axp192.Axp192.SetGPIO0(Iot.Device.Axp192.Gpio0Behavior,System.Byte)</Target>
Expand All @@ -43,6 +57,41 @@
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.get_DeleteTargetAfterTimeout</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.SendWarningMessage(System.String,System.UInt32,System.String,System.DateTimeOffset)</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.set_DeleteTargetAfterTimeout(System.TimeSpan)</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.Sentences.EngineData.#ctor(System.Int32,System.Int32,UnitsNet.RotationalSpeed,UnitsNet.Ratio,System.TimeSpan,System.Nullable{UnitsNet.Temperature})</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.Sentences.SeaSmartEngineDetail.#ctor(System.Boolean,System.TimeSpan,UnitsNet.Temperature,System.Int32)</Target>
<Left>lib/net6.0/Iot.Device.Bindings.dll</Left>
<Right>lib/net6.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Iot.Device.Axp192.BatteryStatus.Overwinered</Target>
Expand All @@ -64,6 +113,20 @@
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Iot.Device.Nmea0183.AisManager.CleanupLatency</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>F:Iot.Device.Nmea0183.AisManager.WarningRepeatTimeout</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Axp192.Axp192.SetGPIO0(Iot.Device.Axp192.Gpio0Behavior,System.Byte)</Target>
Expand All @@ -85,4 +148,39 @@
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.get_DeleteTargetAfterTimeout</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.SendWarningMessage(System.String,System.UInt32,System.String,System.DateTimeOffset)</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.AisManager.set_DeleteTargetAfterTimeout(System.TimeSpan)</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.Sentences.EngineData.#ctor(System.Int32,System.Int32,UnitsNet.RotationalSpeed,UnitsNet.Ratio,System.TimeSpan,System.Nullable{UnitsNet.Temperature})</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Iot.Device.Nmea0183.Sentences.SeaSmartEngineDetail.#ctor(System.Boolean,System.TimeSpan,UnitsNet.Temperature,System.Int32)</Target>
<Left>lib/netstandard2.0/Iot.Device.Bindings.dll</Left>
<Right>lib/netstandard2.0/Iot.Device.Bindings.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
</Suppressions>
5 changes: 5 additions & 0 deletions src/devices/Nmea0183/Ais/AisSafetyState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,10 @@ public enum AisSafetyState
/// The other target is lost, meaning there was no recent position update from that ship.
/// </summary>
Lost,

/// <summary>
/// The other target is so far away or the relative speed is so low, that (T)CPA calculations wouldn't be meaningful.
/// </summary>
FarAway
}
}
31 changes: 26 additions & 5 deletions src/devices/Nmea0183/Ais/AisTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ public GeographicPosition Position
set;
}

/// <summary>
/// Warnings regarding this target are suppressed until this time elapses.
/// Helpful if a specific target is close but well observed and warnings about this target are annoying and disturb from other possible dangers.
/// </summary>
public DateTimeOffset? SuppressionTime
{
get;
set;
}

/// <summary>
/// The relative position and collision information with respect to our ship (e.g. distance, CPA, TCPA)
/// Only valid if calculated by enabling <see cref="AisManager.EnableAisAlarms"/>.
Expand All @@ -80,11 +90,7 @@ public GeographicPosition Position
/// <returns>A string</returns>
public override string ToString()
{
string s = Name ?? string.Empty;
if (string.IsNullOrWhiteSpace(s))
{
s = FormatMmsi();
}
string s = NameOrMssi();

// Note that the special target types (AtoN, SAR, BaseStation) do not use the notification of transceiver types.
if (Position.ContainsValidPosition())
Expand All @@ -95,6 +101,21 @@ public override string ToString()
return s;
}

/// <summary>
/// Returns the name of the ship if available, the MMSI or some other identification otherwise
/// </summary>
/// <returns>A string</returns>
public string NameOrMssi()
{
string s = Name ?? string.Empty;
if (string.IsNullOrWhiteSpace(s))
{
s = FormatMmsi();
}

return s;
}

/// <summary>
/// Returns the MMSI in user-readable format (always 9 digits).
/// The first three non-zero digits are the country code of the target (for a ship, that defines the flag it flies)
Expand Down
9 changes: 5 additions & 4 deletions src/devices/Nmea0183/Ais/AisTargetExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,9 @@ public static List<ShipRelativePosition> RelativePositionsTo(this Ship self, IEn
relativeDirection = (direction - self1.TrueHeading.Value).Normalize(false);
}

// The other is not a ship - Assume static position
if (distance < parameters.WarningDistance)
// The other is not a ship - Assume static position (but make sure a lost target doesn't become a
// dangerous target - we warn about lost targets separately)
if (distance < parameters.WarningDistance && state != AisSafetyState.Lost)
{
state = AisSafetyState.Dangerous;
}
Expand Down Expand Up @@ -159,7 +160,7 @@ public static List<ShipRelativePosition> RelativePositionsTo(this Ship self, IEn
// if the closest point is the first or the last element, we assume it's more than that, and leave the fields empty
if (usedIndex == 0 || usedIndex == thisTrack.Count - 1)
{
retList.Add(new ShipRelativePosition(self, other, distance, direction, AisSafetyState.Unknown, now)
retList.Add(new ShipRelativePosition(self, other, distance, direction, AisSafetyState.FarAway, now)
{
RelativeDirection = relativeDirection,
ClosestPointOfApproach = null,
Expand All @@ -171,7 +172,7 @@ public static List<ShipRelativePosition> RelativePositionsTo(this Ship self, IEn
var pos = new ShipRelativePosition(self, other, distance, direction, state, now)
{
RelativeDirection = relativeDirection,
// Todo: Should subtract the size of both ships here (idealy considering the direction of the ships hulls)
// Todo: Should subtract the size of both ships here (ideally considering the direction of the ships hulls)
ClosestPointOfApproach = minimumDistance,
TimeOfClosestPointOfApproach = timeOfMinimumDistance,
};
Expand Down
52 changes: 42 additions & 10 deletions src/devices/Nmea0183/Ais/TrackEstimationParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,54 +11,86 @@
namespace Iot.Device.Nmea0183.Ais
{
/// <summary>
/// Configurable parameters that define the behavior of the AIS target movement estimation.
/// Configurable parameters that define the behavior of the AIS Manager: Movement estimation, Warning distances, etc.
/// </summary>
[Serializable]
public record TrackEstimationParameters
{
/// <summary>
/// How much time to calculate back
/// How much time to calculate backwards. Default: 20 minutes
/// </summary>
public TimeSpan StartTimeOffset { get; set; } = TimeSpan.FromMinutes(20);

/// <summary>
/// Default step size
/// Default step size. Default: 10 seconds
/// </summary>
public TimeSpan NormalStepSize { get; set; } = TimeSpan.FromSeconds(10);

/// <summary>
/// How much to calculate ahead
/// How much to calculate ahead. Default: 1 hour
/// </summary>
public TimeSpan EndTimeOffset { get; set; } = TimeSpan.FromMinutes(60);

/// <summary>
/// True to issue a warning if no position data for the own ship is available
/// True to issue a warning if no position data for the own ship is available. Default: true
/// </summary>
public bool WarnIfGnssMissing { get; set; } = true;

/// <summary>
/// Time span between AIS safety checks
/// Time span between AIS safety checks. Default: 5 seconds
/// </summary>
public TimeSpan AisSafetyCheckInterval { get; set; } = TimeSpan.FromSeconds(5);

/// <summary>
/// Minimum CPA distance to issue a warning.
/// Minimum CPA distance to issue a warning. Default: 1 nm.
/// </summary>
public Length WarningDistance { get; set; } = Length.FromNauticalMiles(1);

/// <summary>
/// Minimum TCPA to issue a warning (when <see cref="WarningDistance"/> is also reached)
/// Minimum TCPA to issue a warning (when <see cref="WarningDistance"/> is also reached). Default: 10 minutes
/// </summary>
public TimeSpan WarningTime { get; set; } = TimeSpan.FromMinutes(10);

/// <summary>
/// Maximum age of the position record for a given ship to consider it valid.
/// If this is set to a high value, there's a risk of calculating TCPA/CPA based on outdated data.
/// If this is set to a high value, there's a risk of calculating TCPA/CPA based on outdated data. Default: 5 minutes
/// </summary>
public TimeSpan TargetLostTimeout { get; set; } = TimeSpan.FromMinutes(5);

/// <summary>
/// Maximum age of our own position to consider it valid
/// Maximum age of our own position to consider it valid. Default: 20 seconds.
/// </summary>
public TimeSpan MaximumPositionAge { get; set; } = TimeSpan.FromSeconds(20);

/// <summary>
/// Warn if a vessel is lost within this range. Default: 1 nm
/// </summary>
public Length VesselLostWarningRange { get; set; } = Length.FromNauticalMiles(1);

/// <summary>
/// Even if a vessel is lost within <see cref="VesselLostWarningRange"/>, do not warn if
/// the last known speed was less than this. This prevents a lot of warnings from
/// people switching off their AIS while moored. Set to null to disable. Default: 0.5 knots
/// </summary>
public Speed? VesselLostMinSpeed { get; set; } = Speed.FromKnots(0.5);

/// <summary>
/// If a target has not been updated for this time, it is deleted from the list of valid targets.
/// Additionally, client software should consider targets as lost whose <see cref="AisTarget.LastSeen"/> value is older than 5 minutes or so.
/// A value of 0 or less means infinite. Default: 20 minutes.
/// </summary>
public TimeSpan DeleteTargetAfterTimeout { get; set; } = TimeSpan.FromMinutes(20);

/// <summary>
/// Time between repeats of the same warning. If this is set to a short value, the same proximity warning will be shown very often,
/// which is typically annoying. Default: 10 minutes.
/// </summary>
public TimeSpan WarningRepeatTimeout { get; set; } = TimeSpan.FromMinutes(10);

/// <summary>
/// Controls how often lost targets are removed completely from the target list. The timespan after which a target is considered lost
/// is controlled via <see cref="TargetLostTimeout"/>. Default: 25 minutes
/// </summary>
public TimeSpan CleanupLatency { get; set; } = TimeSpan.FromMinutes(25);
}
}
Loading