Skip to content

Commit 35e3057

Browse files
pgrawehrkrwq
andauthored
NMEA Support binding (#1789)
This adds NMEA 0183 parsing and sending features. - Parses a large number of NMEA message types - Can send out all messages as well - Message router to forward messages between different devices or interfaces - Supports forwarding unknown messages - TCP and UDP servers for NMEA - Autopilot support - Fully tested on a real boat. Unit test covers all parsing and string building - Calculator for compass deviations - Basic geodetic functions for WGS84 positions Co-authored-by: Krzysztof Wicher <[email protected]>
1 parent 6fff13e commit 35e3057

File tree

94 files changed

+213008
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+213008
-1
lines changed

src/Iot.Device.Bindings/Iot.Device.Bindings.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
<ProjectReference Include="$(MainLibraryPath)System.Device.Gpio.csproj" />
3636
<PackageReference Include="System.Management" Version="$(SystemManagementPackageVersion)" />
3737
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
38-
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
38+
<PackageReference Include="System.Text.Json" Version="$(SystemTextJsonPackageVersion)" />
39+
</ItemGroup>
40+
<ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
41+
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
3942
</ItemGroup>
4043
<ItemGroup>
4144
<EmbeddedResource Include="../devices/CharacterLcd/BigFontMap.txt" />

src/devices/Common/Common.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@
2828
<ItemGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1' Or '$(TargetFramework)' == 'netstandard2.0'">
2929
<Compile Include="IsExternalInit.cs" />
3030
</ItemGroup>
31+
<ItemGroup Condition="$(TargetFramework) == 'netstandard2.0'">
32+
<PackageReference Include="Microsoft.Bcl.HashCode" Version="1.1.1" />
33+
</ItemGroup>
3134
</Project>
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Globalization;
7+
using System.Linq;
8+
using System.Text;
9+
using UnitsNet;
10+
11+
namespace Iot.Device.Common
12+
{
13+
/// <summary>
14+
/// Provides extension methods for <see cref="UnitsNet.Angle"/>
15+
/// </summary>
16+
public static class AngleExtensions
17+
{
18+
/// <summary>
19+
/// Normalizes the angle so it is between 0° and 360° or between -180° and +180° respectively.
20+
/// </summary>
21+
/// <param name="self">Instance to normalize</param>
22+
/// <param name="toFullCircle">Set to true to normalize to 0-360°, otherwise normalizes to +/-180°</param>
23+
public static Angle Normalize(this Angle self, bool toFullCircle)
24+
{
25+
double r = self.Radians;
26+
if (toFullCircle)
27+
{
28+
if (r > Math.PI * 2)
29+
{
30+
r = r % (Math.PI * 2);
31+
}
32+
33+
if (r < 0)
34+
{
35+
r = -(Math.Abs(r) % (Math.PI * 2));
36+
if (r < 0)
37+
{
38+
r += Math.PI * 2;
39+
}
40+
}
41+
}
42+
else
43+
{
44+
if (r > Math.PI)
45+
{
46+
r = r % (Math.PI * 2);
47+
if (r > Math.PI)
48+
{
49+
// Still above 180?
50+
r -= Math.PI * 2;
51+
}
52+
}
53+
54+
if (r < -Math.PI)
55+
{
56+
r = -(Math.Abs(r) % (Math.PI * 2));
57+
if (r < -Math.PI)
58+
{
59+
r += Math.PI * 2;
60+
}
61+
}
62+
}
63+
64+
// Return in same unit as original input
65+
return Angle.FromRadians(r).ToUnit(self.Unit);
66+
}
67+
68+
/// <summary>
69+
/// Calculate the difference between two angles. Useful to compute the angle error between a desired and an actual track.
70+
/// </summary>
71+
/// <param name="currentTrack">First angle, actual direction</param>
72+
/// <param name="destinationTrack">Second angle, desired direction</param>
73+
/// <returns>The normalized result of <paramref name="currentTrack"/>-<paramref name="destinationTrack"/>. The value is negative if
74+
/// the current track is to port (left) of the the desired track and positive otherwise</returns>
75+
public static Angle Difference(Angle currentTrack, Angle destinationTrack)
76+
{
77+
double val = currentTrack.Radians - destinationTrack.Radians;
78+
return Angle.FromRadians(val).ToUnit(currentTrack.Unit).Normalize(false);
79+
}
80+
81+
/// <summary>
82+
/// Helper method to convert a true angle to a magnetic one, given the variation.
83+
/// </summary>
84+
/// <param name="angleTrue">Course relative to true north</param>
85+
/// <param name="variation">Variation. Positive for east</param>
86+
/// <returns>The magnetic course</returns>
87+
/// <remarks>Remember: From true to false with the wrong sign</remarks>
88+
public static Angle TrueToMagnetic(this Angle angleTrue, Angle variation)
89+
{
90+
return (angleTrue - variation).Normalize(true);
91+
}
92+
93+
/// <summary>
94+
/// Convert magnetic angle to true angle, given the variation
95+
/// </summary>
96+
/// <param name="angleMagnetic">Magnetic north angle</param>
97+
/// <param name="variation">Variation (positive east)</param>
98+
/// <returns>True north angle</returns>
99+
public static Angle MagneticToTrue(this Angle angleMagnetic, Angle variation)
100+
{
101+
return (angleMagnetic + variation).Normalize(true);
102+
}
103+
104+
/// <summary>
105+
/// Calculates the average (medium) of a set of points.
106+
/// See https://en.wikipedia.org/wiki/Mean_of_circular_quantities
107+
/// This method fails if an empty input set is provided or the inputs are evenly distributed over the circle.
108+
/// </summary>
109+
/// <param name="inputAngles">A set of angles</param>
110+
/// <param name="result">The angle that is the mean of the given angles.</param>
111+
/// <returns>True on success, false otherwise.</returns>
112+
public static bool TryAverageAngle(this IEnumerable<Angle> inputAngles, out Angle result)
113+
{
114+
if (inputAngles == null)
115+
{
116+
throw new ArgumentNullException(nameof(inputAngles));
117+
}
118+
119+
var cnt = inputAngles.Count();
120+
if (cnt == 0)
121+
{
122+
result = default;
123+
return false;
124+
}
125+
126+
double sin = 0;
127+
double cos = 0;
128+
foreach (var a in inputAngles)
129+
{
130+
sin += Math.Sin(a.Radians);
131+
cos += Math.Cos(a.Radians);
132+
}
133+
134+
sin /= cnt;
135+
cos /= cnt;
136+
137+
if (sin > 0 && cos > 0)
138+
{
139+
result = Angle.FromRadians(Math.Atan(sin / cos));
140+
return true;
141+
}
142+
143+
if (cos < 0)
144+
{
145+
result = Angle.FromRadians(Math.Atan(sin / cos) + Math.PI);
146+
return true;
147+
}
148+
149+
if (sin < 0 && cos > 0)
150+
{
151+
result = Angle.FromRadians(Math.Atan(sin / cos) + 2 * Math.PI);
152+
return true;
153+
}
154+
155+
// cos == 0
156+
result = default;
157+
return false;
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)