diff options
Diffstat (limited to 'nselib/gps.lua')
-rw-r--r-- | nselib/gps.lua | 126 |
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; |