diff --git a/src/application.vala b/src/application.vala index 8b96d3b..2ef4036 100644 --- a/src/application.vala +++ b/src/application.vala @@ -19,11 +19,11 @@ namespace Hamlocator { public class Application : Adw.Application { private Location location; - private LocationToMaidenhead l2m; + private MaidenheadConverter mc; public Application () { Object (application_id: "eu.fl9.hamlocator", flags: ApplicationFlags.FLAGS_NONE); - l2m = new LocationToMaidenhead(); + mc = new MaidenheadConverter(); } construct { @@ -58,7 +58,7 @@ namespace Hamlocator { win.SetLabel("unable to\nget location"); win.SetDetails(@"Reason:\n$(pos.err)"); } else { - win.SetLabel(l2m.get_locator(pos.loc)); + win.SetLabel(mc.location_to_maidenhead(pos.loc)); win.SetDetails(@"lat: $(pos.loc.latitude)°\nlon: $(pos.loc.longitude)°"); } win.SetRefreshButtonEnabled(true); diff --git a/src/maidenhead.vala b/src/maidenhead.vala index 92859b5..a024011 100644 --- a/src/maidenhead.vala +++ b/src/maidenhead.vala @@ -3,14 +3,13 @@ // https://metacpan.org/release/MEH/Ham-Locator-0.1000/source/lib/Ham/Locator.pm namespace Hamlocator { - public class LocationToMaidenhead : Object { - private string l2m; + public class MaidenheadConverter : Object { + private const char[] L2M = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; + private const double[] M2L_MULTIPLIERS = {10.0, 1.0, 1.0/24.0, 1.0/240.0, 1.0/5760.0}; - public LocationToMaidenhead() { - this.l2m = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - } + public MaidenheadConverter() {} - public string get_locator(GClue.Location pos) { + public string location_to_maidenhead(GClue.Location pos) { double lat = pos.latitude + 90; double lon = pos.longitude + 180; @@ -18,7 +17,7 @@ namespace Hamlocator { double lat1 = (lat / 10); double lon1 = (lon / 20); - string loc1 = @"$(this.l2m[Math.lround(Math.floor(lon1))])$(this.l2m[Math.lround(Math.floor(lat1))])"; + string loc1 = @"$(L2M[Math.lround(Math.floor(lon1))])$(L2M[Math.lround(Math.floor(lat1))])"; // Square double lat2 = 10 * (lat1 - Math.floor(lat1)); @@ -30,9 +29,40 @@ namespace Hamlocator { double lat3 = 24 * (lat2 - Math.floor(lat2)); double lon3 = 24 * (lon2 - Math.floor(lon2)); - string loc3 = @"$(this.l2m[Math.lround(Math.floor(lon3))])$(this.l2m[Math.lround(Math.floor(lat3))])".down(); + string loc3 = @"$(L2M[Math.lround(Math.floor(lon3))])$(L2M[Math.lround(Math.floor(lat3))])".down(); return @"$loc1$loc2$loc3"; } + + public GClue.Location? maidenhead_to_location(string locator) { + double lat = 0.0; + double lon = 0.0; + + if (locator.length % 2 != 0) { + warning("Locator needs to be of even length"); + return null; + } + long i = 0; // where we are in the locator + long mult_i = 0; // which multiplier are we currently using + var upLocator = locator.up(); + while (i < upLocator.length) { + // letters + lon += (upLocator[i++] - 'A') * 2.0 * M2L_MULTIPLIERS[mult_i]; + lat += (upLocator[i++] - 'A') * M2L_MULTIPLIERS[mult_i]; + mult_i++; + if (i >= upLocator.length) { break; } + // numbers + lon += (upLocator[i++] - '0') * 2.0 * M2L_MULTIPLIERS[mult_i]; + lat += (upLocator[i++] - '0') * M2L_MULTIPLIERS[mult_i]; + mult_i++; + } + + // move to the center of the box + mult_i--; + lon += 0.5 * 2.0 * M2L_MULTIPLIERS[mult_i]; + lat += 0.5 * M2L_MULTIPLIERS[mult_i]; + + return new SimpleLocation((lat - 90.0), (lon - 180.0)); + } } } diff --git a/src/meson.build b/src/meson.build index e137ac4..491eac3 100644 --- a/src/meson.build +++ b/src/meson.build @@ -2,14 +2,14 @@ hamlocator_sources = [ 'window.vala', 'application.vala', 'location.vala', - 'maidenhead.vala' + 'maidenhead.vala', + 'simple_location.vala' ] test_sources = [ 'tests' / 'main.vala', 'tests' / 'testcase.vala', 'tests' / 'maidenhead_tests.vala', - 'tests' / 'fake_geoclue_location.vala' ] hamlocator_deps = [ diff --git a/src/tests/fake_geoclue_location.vala b/src/simple_location.vala similarity index 61% rename from src/tests/fake_geoclue_location.vala rename to src/simple_location.vala index 692a982..c0df7c9 100644 --- a/src/tests/fake_geoclue_location.vala +++ b/src/simple_location.vala @@ -1,37 +1,37 @@ -public class FakeGeoclueLocation : GLib.Object, GClue.Location { +public class SimpleLocation : GLib.Object, GClue.Location { private double lat; private double lon; - - public FakeGeoclueLocation(double lat, double lon) { + + public SimpleLocation(double lat, double lon) { this.lat = lat; this.lon = lon; } - - public override double accuracy { + + public override double accuracy { get { return double.NAN; } set { assert(false); } } - public override double altitude { + public override double altitude { get { return double.NAN; } set { assert(false); } } - public override string description { owned + public override string description { owned get { return ""; } set { assert(false); } } - public override double heading { + public override double heading { get { return double.NAN; } set { assert(false); } } - public override double latitude { + public override double latitude { get { return this.lat; } set { assert(false); } } - public override double longitude { + public override double longitude { get { return this.lon; } set { assert(false); } } - public override double speed { + public override double speed { get { return double.NAN; } set { assert(false); } } diff --git a/src/tests/maidenhead_tests.vala b/src/tests/maidenhead_tests.vala index d7a63c2..48c4e79 100644 --- a/src/tests/maidenhead_tests.vala +++ b/src/tests/maidenhead_tests.vala @@ -1,45 +1,51 @@ public class MaidenheadTests : TestCase { public MaidenheadTests() { base("Maidenhead Tests"); - add_test_location(0.0, 0.0, "JJ00aa"); - add_test_location(90.0, 0.0, "JS00aa"); - add_test_location(0.0, 180.0, "SJ00aa"); - add_test_location(90.0, 180.0, "SS00aa"); - add_test_location(-90.0, 0.0, "JA00aa"); - add_test_location(0.0, -180.0, "AJ00aa"); - add_test_location(-90.0, -180.0, "AA00aa"); - add_test_location(90.0, -180.0, "AS00aa"); - add_test_location(-90.0, 180.0, "SA00aa"); + add_test_maidenhead(0.0, 0.0, "JJ00aa"); + add_test_maidenhead(90.0, 0.0, "JS00aa"); + add_test_maidenhead(0.0, 180.0, "SJ00aa"); + add_test_maidenhead(90.0, 180.0, "SS00aa"); + add_test_maidenhead(-90.0, 0.0, "JA00aa"); + add_test_maidenhead(0.0, -180.0, "AJ00aa"); + add_test_maidenhead(-90.0, -180.0, "AA00aa"); + add_test_maidenhead(90.0, -180.0, "AS00aa"); + add_test_maidenhead(-90.0, 180.0, "SA00aa"); - add_test_location(51.0, 17.0, "JO80lx"); - add_test_location(51.1, 17.1, "JO81nc"); - add_test_location(51.2, 17.2, "JO81oe"); - add_test_location(51.3, 17.3, "JO81ph"); - add_test_location(51.4, 17.4, "JO81qj"); - add_test_location(51.5, 17.5, "JO81sm"); - add_test_location(51.6, 17.6, "JO81to"); - add_test_location(51.7, 17.7, "JO81uq"); - add_test_location(51.8, 17.8, "JO81vt"); - add_test_location(51.9, 17.9, "JO81wv"); - add_test_location(51.99, 17.99, "JO81xx"); + add_test_maidenhead(51.0, 17.0, "JO80lx"); + add_test_maidenhead(51.1, 17.1, "JO81nc"); + add_test_maidenhead(51.2, 17.2, "JO81oe"); + add_test_maidenhead(51.3, 17.3, "JO81ph"); + add_test_maidenhead(51.4, 17.4, "JO81qj"); + add_test_maidenhead(51.5, 17.5, "JO81sm"); + add_test_maidenhead(51.6, 17.6, "JO81to"); + add_test_maidenhead(51.7, 17.7, "JO81uq"); + add_test_maidenhead(51.8, 17.8, "JO81vt"); + add_test_maidenhead(51.9, 17.9, "JO81wv"); + add_test_maidenhead(51.99, 17.99, "JO81xx"); - add_test_location(51.1205, 17.0261, "JO81mc"); - add_test_location(37.1104, -5.4932, "IM77gc"); - add_test_location(-30.5377, 22.8516, "KF19kl"); - add_test_location(-27.4613, -65.0391, "FG72lm"); - add_test_location(-24.6168, 136.4063, "PG85ej"); + add_test_maidenhead(51.1205, 17.0261, "JO81mc"); + add_test_maidenhead(37.1104, -5.4932, "IM77gc"); + add_test_maidenhead(-30.5377, 22.8516, "KF19kl"); + add_test_maidenhead(-27.4613, -65.0391, "FG72lm"); + add_test_maidenhead(-24.6168, 136.4063, "PG85ej"); } - protected Hamlocator.LocationToMaidenhead tested; + protected Hamlocator.MaidenheadConverter tested; public override void set_up() { - this.tested = new Hamlocator.LocationToMaidenhead(); + this.tested = new Hamlocator.MaidenheadConverter(); } - public void add_test_location(double lat, double lon, string expected) { - add_test (@"Expect location lat:$lat lon:$lon to be $expected", () => { - GClue.Location pos = new FakeGeoclueLocation(lat, lon); - assert_cmpstr(this.tested.get_locator(pos), EQ, expected); + public void add_test_maidenhead(double lat, double lon, string locator) { + add_test (@"Expect location lat:$lat lon:$lon to be $locator", () => { + GClue.Location pos = new SimpleLocation(lat, lon); + assert_cmpstr(this.tested.location_to_maidenhead(pos), EQ, locator); + }); + add_test (@"Expect location at $locator to be approx. lat:$lat lon:$lon", () => { + GClue.Location pos = this.tested.maidenhead_to_location(locator); + assert_nonnull(pos); + assert_cmpfloat_with_epsilon(pos.latitude, lat, (1.0/24.0)); + assert_cmpfloat_with_epsilon(pos.longitude, lon, (2.0/24.0)); }); } }