summaryrefslogtreecommitdiffstats
path: root/nselib/gps.lua
diff options
context:
space:
mode:
Diffstat (limited to 'nselib/gps.lua')
-rw-r--r--nselib/gps.lua126
1 files changed, 126 insertions, 0 deletions
diff --git a/nselib/gps.lua b/nselib/gps.lua
new file mode 100644
index 0000000..a4528ce
--- /dev/null
+++ b/nselib/gps.lua
@@ -0,0 +1,126 @@
+local os = require "os"
+local stdnse = require "stdnse"
+local string = require "string"
+local unittest = require "unittest"
+_ENV = stdnse.module("gps", stdnse.seeall)
+
+---
+-- A smallish gps parsing module.
+-- Currently does GPRMC NMEA decoding
+--
+-- @author Patrik Karlsson <patrik@cqure.net>
+--
+--
+
+NMEA = {
+
+ -- Parser for the RMC sentence
+ RMC = {
+
+ parse = function(str)
+
+ local time, status, latitude, ns_indicator, longitude,
+ ew_indicator, speed, course, date, variation,
+ ew_variation, checksum = str:match("^%$GPRMC,([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^%*]*)(.*)$")
+
+ if ( not(latitude) or not(longitude) ) then
+ return
+ end
+
+ local deg, min = latitude:match("^(..)(.*)$")
+ if ( not(deg) or not(min) ) then
+ return
+ end
+ latitude = tonumber(deg) + (tonumber(min)/60)
+
+ deg, min = longitude:match("^(..)(.*)$")
+ if ( not(deg) or not(min) ) then
+ return
+ end
+ longitude = tonumber(deg) + (tonumber(min)/60)
+ if ( ew_indicator == 'W' ) then
+ longitude = -longitude
+ end
+
+ if ( ns_indicator == 'S' ) then
+ latitude = -latitude
+ end
+
+ return { time = time, status = status, latitude = latitude,
+ longitude = longitude, speed = speed, course = course,
+ date = date, variation = variation,
+ ew_variation = ew_variation }
+ end,
+
+ },
+
+ -- Calculates an verifies the message checksum
+ --
+ -- @param str containing the GPS sentence
+ -- @return status true on success, false if the checksum does not match
+ -- @return err string if status is false
+ checksum = function(str)
+ local val = 0
+ for c in str:sub(2,-4):gmatch(".") do
+ val = val ~ string.byte(c)
+ end
+
+ if ( str:sub(-2):upper() ~= stdnse.tohex(string.char(val)):upper() ) then
+ return false, ("Failed to verify checksum (got: %s; expected: %s)"):format(stdnse.tohex(string.char(val)), str:sub(-2))
+ end
+ return true
+ end,
+
+ -- Parses a GPS sentence using the appropriate parser
+ --
+ -- @param str containing the GPS sentence
+ -- @return entry table containing the parsed response or
+ -- err string if status is false
+ -- @return status true on success, false on failure
+ parse = function(str)
+
+ local status, err = NMEA.checksum(str)
+ if ( not(status) ) then
+ return false, err
+ end
+
+ local prefix = str:match("^%$GP([^,]*)")
+ if ( not(prefix) ) then
+ return false, "Not a NMEA sentence"
+ end
+
+ if ( NMEA[prefix] and NMEA[prefix].parse ) then
+ local e = NMEA[prefix].parse(str)
+ if (not(e)) then
+ return false, ("Failed to parse entry: %s"):format(str)
+ end
+ return true, e
+ else
+ local err = ("No parser for prefix: %s"):format(prefix)
+ stdnse.debug2("%s", err)
+ return false, err
+ end
+
+ end
+
+}
+
+Util = {
+
+ convertTime = function(date, time)
+ local d = {}
+ d.hour, d.min, d.sec = time:match("(..)(..)(..)")
+ d.day, d.month, d.year = date:match("(..)(..)(..)")
+ d.year = d.year + 2000
+ return os.time(d)
+ end
+}
+
+if not unittest.testing() then
+ return _ENV
+end
+
+test_suite = unittest.TestSuite:new()
+test_suite:add_test(unittest.is_true(NMEA.checksum("$GPGLL,5300.97914,N,00259.98174,E,125926,A*28")), "valid checksum")
+test_suite:add_test(unittest.is_false(NMEA.checksum("$XPGLL,5300.97914,N,00259.98174,E,125926,A*28")), "invalid checksum")
+return _ENV;