1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
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;
|