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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#!/usr/bin/env python
#
# Analyze one or more NMEA or JSON files to determine the cycle sequence of
# sentences. JSON files must have been emitted by a driver without the
# CYCLE_END_RELIABLE capability, otherwise the results will be meaningless.
import sys, getopt, json
verbose = 0
class analyze_error:
def __init__(self, filename, lineno, msg):
self.filename = filename
self.lineno = lineno
self.msg = msg
def str(self):
return '"%s", line %d: %s' % (filename, lineno, msg)
def extract_from_nmea(filename, lineno, line):
"Extend sequence of tag/timestamp tuples from an NMEA sentence"
hhmmss = {
"$GPRMC": 1,
"$GPGLL": 5,
"$GPGGA": 1,
"$GPGBS": 1,
"$GPZDA": 1,
"$PASHR": 4,
}
fields = line.split(",")
tag = fields[0][1:]
if fields[0] in hhmmss:
timestamp = fields[hhmmss[fields[0]]]
return [(tag, timestamp)]
else:
return []
def extract_from_json(filename, lineno, line):
"Extend sequence of tag/timestamp tuples from a JSON dump of a sentence"
# FIXME: Analyze JSON sentences
raise analyze_error(filename, lineno, "JSON analsis not yet implemented.")
def extract_timestamped_sentences(fp):
"Do the basic work of extracting tags and timestamps"
sequence = []
filetype = None
lineno = 0
while True:
line = fp.readline()
if not line:
break
lineno += 1
if line.startswith("#"):
continue
elif filetype == None:
if line.startswith("$"):
filetype = "NMEA"
elif line.startswith("{"):
filetype = "JSON"
else:
raise analyze_error(fp.name, lineno, "unknown file type.")
if verbose:
print "cycle_analyzer: %s is %s" % (fp.name, filetype)
# The reason for this odd logic is that we want to oock onto
# either (a) analyzing NMEA only, or (b) analyzing JSON only
# and ignoring NMEA, depending on which kind the first data
# line of the file is. This gives the ability to run against
# either raw NMEA or regression-test .chk files generated by
# binary-format devices, without haviong to run gpsd to
# do reanalysis.
if filetype == "NMEA" and line.startswith("$"):
sequence += extract_from_nmea(fp.name, lineno, line)
elif filetype == "JSON" and line.startswith("{"):
sequence + extract_from_json(fp.name, lineno, line)
else:
sys.stderr.write("cycle_analyzer: non-NMEA, non-JSON data %s.\n" % line)
return []
return sequence
def analyze(fp, stage):
"Analyze the cycle sequence of a device from its output logs."
# First, extract tags and timestamps
sequence = extract_timestamped_sentences(fp)
if stage == "extract":
for (tag, timestamp) in sequence:
print "%s: %s" % (tag, timestamp)
# Then, do cycle detection
events = []
out_of_order = False
for i in range(len(sequence)-1):
(this_tag, this_time) = sequence[i]
(next_tag, next_time) = sequence[i+1]
if float(this_time) == 0:
continue
events.append(this_tag)
if float(this_time) < float(next_time):
events.append("-")
if float(this_time) < float(next_time):
out_of_order = True
if out_of_order:
sys.stderr.write("cycle_analyzer: some timestamps occur out of order in %s\n" % fp.name)
# We need 6 cycles because the first and last might be incomplete, and we
# need at least 4 cycles in the middle to have two full ones om
# split-cycle devices like old Garmins.
if events.count("-") < 6:
sys.stderr.write("cycle_analyzer: fewer than 5 cycles in\n" % fp.name)
if stage == "events":
for event in events:
print event
# Now group events into bursts
bursts = []
current = []
for event in events + ['-']:
if event == '-':
bursts.append(tuple(current))
current = []
else:
current.append(event)
if stage == "bursts":
for burst in bursts:
print burst
# FIXME: more analysis goes here
if __name__ == "__main__":
stage = None
try:
(options, arguments) = getopt.getopt(sys.argv[1:], "s:v")
for (switch, val) in options:
if (switch == '-s'):
stage = val # Stop at specicified stage
elif (switch == '-v'):
verbose += 1
except getopt.GetoptError, msg:
print "cycle_analyzer: " + str(msg)
raise SystemExit, 1
try:
if arguments:
for filename in arguments:
fp = open(filename)
analyze(fp, stage)
fp.close()
else:
analyze(sys.stdin)
except analyze_error, e:
print e
raise SystemExit, 1
|