summaryrefslogtreecommitdiff
path: root/devtools/cycle_analyzer
blob: a205f60256905fb1c01a26d95cded1ae48fe963d (plain)
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