Skip to content

Commit

Permalink
Fix GeoPatch algorithm (#264)
Browse files Browse the repository at this point in the history
* Update geo patch calculation and correct out of bound index
* Improve tests to check near location city
* Fixes #253
  • Loading branch information
Neylix committed Apr 19, 2022
1 parent b1f8494 commit aedde02
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 61 deletions.
1 change: 1 addition & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ config :archethic, ArchEthic.P2P.Listener, enabled: false
config :archethic, ArchEthic.P2P.MemTableLoader, enabled: false
config :archethic, ArchEthic.P2P.MemTable, enabled: false
config :archethic, ArchEthic.P2P.Client, MockClient
config :archethic, ArchEthic.P2P.GeoPatch.GeoIP, MockGeoIP

config :archethic, ArchEthic.P2P.BootstrappingSeeds, enabled: false

Expand Down
87 changes: 31 additions & 56 deletions lib/archethic/p2p/geo_patch.ex
Original file line number Diff line number Diff line change
Expand Up @@ -24,73 +24,48 @@ defmodule ArchEthic.P2P.GeoPatch do
end

defp compute_random_patch do
list_char = Enum.concat([?0..?9, ?A..?F])
Enum.take_random(list_char, 3) |> List.to_string()
list_char1 = Enum.concat([?0..?9, ?A..?F])
list_char2 = Enum.concat([?0..?3, ?C..?F])

Enum.take_random(list_char1, 2)
|> List.insert_at(1, Enum.take_random(list_char2, 1))
|> List.to_string()
end

defp compute_patch(lat, lon) do
lat_sign = sign(lat)
lon_sign = sign(lon)

fdc = [lat / 90, lon / 180]

sd =
[(lat - lat_sign * 45) / 2, (lon - lon_sign * 90) / 2]
|> resolve_with_sign([lat, lon])
# convert 90 and 180 to -90 and -180 to not get an out of bound index
lat = if(lat == 90, do: -90) || lat
lon = if(lon == 180, do: -180) || lon

sdc = [List.first(sd) / 22.5, List.last(sd) / 45]
lon_pos = (lon + 180) / 22.5
# Adding 4 to have second digit hex value from C to 3
lat_pos = (lat + 90) / 22.5 + 4

td =
[
(List.first(sd) - lat_sign * 11.25) / 2,
(List.last(sd) - lon_sign * 22.5) / 2
]
|> resolve_with_sign(sd)
first_digit = main_index_patch(trunc(lon_pos))
second_digit = main_index_patch(trunc(lat_pos))

tdc = [List.first(td) / 5.625, List.last(td) / 11.25]
lat_precision = ((lat_pos - trunc(lat_pos)) / 0.25) |> trunc()
lon_precision = ((lon_pos - trunc(lon_pos)) / 0.25) |> trunc()

patch =
[index_patch(fdc), index_patch(sdc), index_patch(tdc)]
|> Enum.join("")
third_digit = precision_index_patch(lat_precision, lon_precision)

patch
[first_digit, second_digit, third_digit]
|> Enum.join("")
end

defp index_patch([f_i, s_i]) when f_i > 0.5 and f_i <= 1 and s_i < -0.5 and s_i >= -1, do: '0'
defp index_patch([f_i, s_i]) when f_i > 0.5 and f_i <= 1 and s_i < 0 and s_i >= -0.5, do: '1'
defp index_patch([f_i, s_i]) when f_i > 0.5 and f_i <= 1 and s_i < 0.5 and s_i >= 0, do: '2'
defp index_patch([f_i, s_i]) when f_i > 0.5 and f_i <= 1 and s_i < 1 and s_i >= 0.5, do: '3'

defp index_patch([f_i, s_i]) when f_i > 0 and f_i <= 0.5 and s_i < -0.5 and s_i >= -1, do: '4'
defp index_patch([f_i, s_i]) when f_i > 0 and f_i <= 0.5 and s_i < 0 and s_i >= -0.5, do: '5'
defp index_patch([f_i, s_i]) when f_i > 0 and f_i <= 0.5 and s_i < 0.5 and s_i >= 0, do: '6'
defp index_patch([f_i, s_i]) when f_i > 0 and f_i <= 0.5 and s_i < 1 and s_i >= 0.5, do: '7'

defp index_patch([f_i, s_i]) when f_i > -0.5 and f_i <= 0 and s_i < -0.5 and s_i >= -1, do: '8'
defp index_patch([f_i, s_i]) when f_i > -0.5 and f_i <= 0 and s_i < 0 and s_i >= -0.5, do: '9'
defp index_patch([f_i, s_i]) when f_i > -0.5 and f_i <= 0 and s_i < 0.5 and s_i >= 0, do: 'A'
defp index_patch([f_i, s_i]) when f_i > -0.5 and f_i <= 0 and s_i < 1 and s_i >= 0.5, do: 'B'

defp index_patch([f_i, s_i]) when f_i > -1 and f_i <= -0.5 and s_i < -0.5 and s_i >= -1, do: 'C'
defp index_patch([f_i, s_i]) when f_i > -1 and f_i <= -0.5 and s_i < 0 and s_i >= -0.5, do: 'D'
defp index_patch([f_i, s_i]) when f_i > -1 and f_i <= -0.5 and s_i < 0.5 and s_i >= 0, do: 'E'
defp index_patch([f_i, s_i]) when f_i > -1 and f_i <= -0.5 and s_i < 1 and s_i >= 0.5, do: 'F'

defp sign(number) when number < 0, do: -1
defp sign(number) when number >= 0, do: 1

defp resolve_with_sign([first, second], [first2, second2]) do
[
do_resolve_with_sign(first, first2),
do_resolve_with_sign(second, second2)
]
defp main_index_patch(index) do
{'8', '9', 'A', 'B', 'C', 'D', 'E', 'F', '0', '1', '2', '3', '4', '5', '6', '7'}
|> elem(index)
end

defp do_resolve_with_sign(x1, x2) do
if sign(x1) == sign(x2) do
x1
else
x2 / 2
end
defp precision_index_patch(index1, index2) do
{
{'0', '1', '2', '3'},
{'4', '5', '6', '7'},
{'8', '9', 'A', 'B'},
{'C', 'D', 'E', 'F'}
}
|> elem(index1)
|> elem(index2)
end
end
3 changes: 3 additions & 0 deletions test/archethic/bootstrap_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,9 @@ defmodule ArchEthic.BootstrapTest do
MockDB
|> stub(:get_first_public_key, fn _ -> first_public_key end)

MockGeoIP
|> stub(:get_coordinates, fn {200, 50, 20, 10} -> {0.0, 0.0} end)

assert :ok =
Bootstrap.run(
{200, 50, 20, 10},
Expand Down
40 changes: 35 additions & 5 deletions test/archethic/p2p/geo_patch_test.exs
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
defmodule ArchEthic.P2P.GeoPatchTest do
@moduledoc """
This module defines the test case to be used by
geopatch tests.
"""

use ExUnit.Case

alias ArchEthic.P2P.GeoPatch

import Mox

test "from_ip/1 should compute patch from coordinates" do
expect(MockGeoIP, :get_coordinates, fn ip ->
stub(MockGeoIP, :get_coordinates, fn ip ->
case ip do
# Spain (Alicante)
{88, 22, 30, 229} ->
Expand All @@ -23,12 +28,37 @@ defmodule ArchEthic.P2P.GeoPatchTest do
# Switzerland (Zurich)
{109, 164, 214, 168} ->
{47.366670, 8.550000}

# Edge value
{1, 2, 3, 4} ->
{90, 180}

# France (Bordeaux)
{1, 1, 1, 1} ->
{44.828114, -0.584424}

# France (Limoges)
{2, 2, 2, 2} ->
{45.819792, 1.256239}

# US (Las Vegas)
{3, 3, 3, 3} ->
{36.165362, -115.102552}

# US (Phoenix)
{4, 4, 4, 4} ->
{33.456609, -112.033383}
end
end)

assert "511" == GeoPatch.from_ip({88, 22, 30, 229})
assert "500" == GeoPatch.from_ip({161, 235, 112, 33})
assert "410" == GeoPatch.from_ip({15, 62, 246, 57})
assert "266" == GeoPatch.from_ip({109, 164, 214, 168})
assert "F1B" == GeoPatch.from_ip({88, 22, 30, 229})
assert "C1D" == GeoPatch.from_ip({161, 235, 112, 33})
assert "A1A" == GeoPatch.from_ip({15, 62, 246, 57})
assert "021" == GeoPatch.from_ip({109, 164, 214, 168})
assert "8C0" == GeoPatch.from_ip({1, 2, 3, 4})
assert "F1F" == GeoPatch.from_ip({1, 1, 1, 1})
assert "020" == GeoPatch.from_ip({2, 2, 2, 2})
assert "A1B" == GeoPatch.from_ip({3, 3, 3, 3})
assert "B14" == GeoPatch.from_ip({4, 4, 4, 4})
end
end

0 comments on commit aedde02

Please sign in to comment.