Packages

An OTP application for reading and parsing GPS data written in Elixir. Will attach to an serial port, and provide positions to subscribers. Distributes positions using GenStage.

Current section

Files

Jump to
xgps lib tools.ex
Raw

lib/tools.ex

defmodule XGPS.Tools do
require Bitwise
def calculate_checksum text do
Enum.reduce(String.codepoints(text), 0, &xor/2)
end
defp xor(x, acc) do
<<val::utf8>> = x
Bitwise.bxor(acc, val)
end
def hex_string_to_int(string) do
string |> Base.decode16! |> :binary.decode_unsigned
end
def int_to_hex_string(int) do
int |> :binary.encode_unsigned |> Base.encode16
end
def lat_to_decimal_degrees(degrees, minutes, "N"), do: degrees + (minutes/60.0)
def lat_to_decimal_degrees(degrees, minutes, "S"), do: (degrees + (minutes/60.0)) * (-1.0)
def lon_to_decimal_degrees(degrees, minutes, "E"), do: degrees + (minutes/60.0)
def lon_to_decimal_degrees(degrees, minutes, "W"), do: (degrees + (minutes/60.0)) * (-1.0)
def lat_from_decimal_degrees(decimal_degrees) when decimal_degrees >= 0.0 do
degrees = Float.floor(decimal_degrees) |> round
minutes = (decimal_degrees - degrees) * 60.0
bearing = "N"
{degrees, minutes, bearing}
end
def lat_from_decimal_degrees(decimal_degrees) when decimal_degrees < 0.0 do
degrees = Float.ceil(decimal_degrees) * (-1.0) |> round
minutes = (decimal_degrees + degrees) * -60.0
bearing = "S"
{degrees, minutes, bearing}
end
def lon_from_decimal_degrees(decimal_degrees) when decimal_degrees >= 0.0 do
degrees = Float.floor(decimal_degrees) |> round
minutes = (decimal_degrees - degrees) * 60.0
bearing = "E"
{degrees, minutes, bearing}
end
def lon_from_decimal_degrees(decimal_degrees) when decimal_degrees < 0.0 do
degrees = Float.ceil(decimal_degrees) * (-1.0) |> round
minutes = (decimal_degrees + degrees) * -60.0
bearing = "W"
{degrees, minutes, bearing}
end
def to_gps_date(date) do
year = "#{date.year}" |> String.slice(2,2)
month = "#{date.month}" |> String.pad_leading(2,"0")
day = "#{date.day}" |> String.pad_leading(2,"0")
day <> month <> year
end
def to_gps_time(time) do
hour = "#{time.hour}" |> String.pad_leading(2,"0")
minute = "#{time.minute}" |> String.pad_leading(2,"0")
second = "#{time.second}" |> String.pad_leading(2,"0")
{micro, _} = time.microsecond
ms = round(micro / 1000)
millis = "#{ms}" |> String.pad_leading(3, "0")
"#{hour}#{minute}#{second}." <> millis
end
def generate_rmc_and_gga_for_simulation(lat, lon, alt, date_time) do
{lat_deg, lat_min, lat_bear} = XGPS.Tools.lat_from_decimal_degrees(lat)
{lon_deg, lon_min, lon_bear} = XGPS.Tools.lon_from_decimal_degrees(lon)
latitude = lat_to_string(lat_deg, lat_min, lat_bear)
longitude = lon_to_string(lon_deg, lon_min, lon_bear)
date = XGPS.Tools.to_gps_date(date_time)
time = XGPS.Tools.to_gps_time(date_time)
rmc_body = "GPRMC,#{time},A,#{latitude},#{longitude},0.0,0.0,#{date},,,A"
rmc_checksum = XGPS.Tools.calculate_checksum(rmc_body) |> XGPS.Tools.int_to_hex_string
rmc = "$#{rmc_body}*#{rmc_checksum}"
gga_body = "GPGGA,#{time},#{latitude},#{longitude},1,05,0.0,#{alt},M,0.0,M,,"
gga_checksum = XGPS.Tools.calculate_checksum(gga_body) |> XGPS.Tools.int_to_hex_string
gga = "$#{gga_body}*#{gga_checksum}"
{rmc, gga}
end
def generate_rmc_and_gga_for_simulation_no_fix(date_time) do
date = XGPS.Tools.to_gps_date(date_time)
time = XGPS.Tools.to_gps_time(date_time)
rmc_body = "GPRMC,#{time},V,,,,,,,#{date},,,A"
rmc_checksum = XGPS.Tools.calculate_checksum(rmc_body) |> XGPS.Tools.int_to_hex_string
rmc = "$#{rmc_body}*#{rmc_checksum}"
gga_body = "GPGGA,#{time},,,,,0,0,,,M,,M,,"
gga_checksum = XGPS.Tools.calculate_checksum(gga_body) |> XGPS.Tools.int_to_hex_string
gga = "$#{gga_body}*#{gga_checksum}"
{rmc, gga}
end
defp lat_to_string(deg, min, bearing) when min >= 10.0 do
deg_string = "#{deg}" |> String.pad_leading(2, "0")
min_string = "#{Float.round(min,4)}" |> String.pad_trailing(7, "0")
deg_string <> min_string <> "," <> bearing
end
defp lat_to_string(deg, min, bearing) do
deg_string = "#{deg}" |> String.pad_leading(2, "0")
min_string = "0#{Float.round(min,4)}" |> String.pad_trailing(7, "0")
deg_string <> min_string <> "," <> bearing
end
defp lon_to_string(deg, min, bearing) when min > 10.0 do
deg_string = "#{deg}" |> String.pad_leading(3, "0")
min_string = "#{Float.round(min,4)}" |> String.pad_trailing(7, "0")
deg_string <> min_string <> "," <> bearing
end
defp lon_to_string(deg, min, bearing) do
deg_string = "#{deg}" |> String.pad_leading(3, "0")
min_string = "0#{Float.round(min,4)}" |> String.pad_trailing(7, "0")
deg_string <> min_string <> "," <> bearing
end
end