Skip to content
Merged
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
62 changes: 55 additions & 7 deletions src/AprsParser/Position.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
namespace AprsSharp.AprsParser
{
using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
Expand Down Expand Up @@ -167,23 +168,70 @@
throw new ArgumentNullException(nameof(coords));
}

Ambiguity = 0;

// first try uncompressed
Match match = Regex.Match(coords, RegexStrings.PositionLatLongWithSymbols);
match.AssertSuccess("Coordinates", nameof(coords));

Ambiguity = 0;
double latitude = DecodeLatitude(match.Groups[1].Value);
double longitude = DecodeLongitude(match.Groups[3].Value);
if (match.Success)
{
double latitude = DecodeLatitude(match.Groups[1].Value);
double longitude = DecodeLongitude(match.Groups[3].Value);

SymbolTableIdentifier = match.Groups[2].Value[0];
SymbolCode = match.Groups[4].Value[0];
Coordinates = new GeoCoordinate(latitude, longitude);
SymbolTableIdentifier = match.Groups[2].Value[0];
SymbolCode = match.Groups[4].Value[0];
Coordinates = new GeoCoordinate(latitude, longitude);
return;
}

// next try compressed
match = Regex.Match(coords, RegexStrings.CompressedPosition);

if (match.Success)
{
double latitude = DecodeCompressedLatitude(match.Groups[2].Value);
double longitude = DecodeCompressedLongitude(match.Groups[3].Value);
SymbolTableIdentifier = match.Groups[1].Value[0];
SymbolCode = match.Groups[4].Value[0];
Coordinates = new GeoCoordinate(latitude, longitude);
return;
}
}

private static int DecodeBase91(string encoded)
{
var bytes = Encoding.ASCII.GetBytes(encoded);

var result = 0;
for (var i = 0; i < bytes.Length; i++)
{
result += i == bytes.Length - 1
? bytes[i] - 33
: (bytes[i] - 33) * (int)Math.Pow(91, bytes.Length - i - 1);
}

return result;
}

private static double DecodeCompressedLatitude(string coords)
{
Debug.Assert(coords.Length == 4, "Compressed latitude must be 4 characters");
var latitude = 90 - (DecodeBase91(coords) / 380926.0);
return Math.Round(latitude, 4);
}

private static double DecodeCompressedLongitude(string coords)
{
Debug.Assert(coords.Length == 4, "Compressed longitude must be 4 characters");
var longitude = -180 + (DecodeBase91(coords) / 190463.0);
return Math.Round(longitude, 4);
}

/// <summary>
/// Decode from Maidenhead Location System gridsquare to a point in the middle of the gridsquare.
/// </summary>
/// <param name="gridsquare">4 or 6 char Maidenhead gridsquare, optionally followed by symbol table and identifier.</param>
public void DecodeMaidenhead(string gridsquare)

Check failure on line 234 in src/AprsParser/Position.cs

View workflow job for this annotation

GitHub Actions / build

Check failure on line 234 in src/AprsParser/Position.cs

View workflow job for this annotation

GitHub Actions / build

{
if (gridsquare == null)
{
Expand Down
12 changes: 12 additions & 0 deletions src/AprsParser/RegexStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@
/// </summary>
public const string PositionLatLongWithSymbols = @"([0-9 \.NS]{8})(.)([0-9 \.EW]{9})(.)";

/// <summary>
/// Matchdes a compressed latitude and longitude with optional additional data
/// Six matches:
/// Symbol table ID
/// Compressed Latitude
/// Compressed Longitude
/// Symbol code
/// Compressed one of: course/speed, radio range, or altitude
/// Compressed data type

Check failure on line 45 in src/AprsParser/RegexStrings.cs

View workflow job for this annotation

GitHub Actions / build

Check failure on line 45 in src/AprsParser/RegexStrings.cs

View workflow job for this annotation

GitHub Actions / build

/// </summary>
public const string CompressedPosition = @"(.)(.{4})(.{4})(.)(.{2})(.)";

/// <summary>
/// Same as <see cref="MaidenheadGridWithOptionalSymbols"/> but forces full line match.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions test/AprsParserUnitTests/PositionUnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,7 @@ public void DecodeLongitudeWrongDecimalPointLocation()
[InlineData(null, '\\', '.', 0, 0)] // defaults
[InlineData("4903.50N/07201.75W-", '/', '-', 49.0583, -72.0292)] // from APRS spec
[InlineData("4903.50S/07201.75E-", '/', '-', -49.0583, 72.0292)] // Ensure south and east work
[InlineData("/5L!!<*e7>7P[", '/', '>', 49.5, -72.7500)] // from the APRS spec
public void Decode(
string? encodedPosition,
char expectedSymbolTable,
Expand Down
Loading