diff options
author | Greg Troxel <gdt@ir.bbn.com> | 2009-12-02 15:47:06 +0000 |
---|---|---|
committer | Greg Troxel <gdt@ir.bbn.com> | 2009-12-02 15:47:06 +0000 |
commit | 96d59fdac2b9eee55171ade78a729331a9f92197 (patch) | |
tree | a62c813a44d9e3e3ba218750adba8b017bbd77a1 /gpsprof | |
parent | 36d56a924abac976402b0ef0b72703009ab649b6 (diff) | |
download | gpsd-96d59fdac2b9eee55171ade78a729331a9f92197.tar.gz |
Substitute $PYTHON instead of assuming 'python' is python.
Now 'make check' runs on NetbSD, and $(prefix)/bin/gpsfake works.
Diffstat (limited to 'gpsprof')
-rwxr-xr-x | gpsprof | 481 |
1 files changed, 0 insertions, 481 deletions
diff --git a/gpsprof b/gpsprof deleted file mode 100755 index 8410f19e..00000000 --- a/gpsprof +++ /dev/null @@ -1,481 +0,0 @@ -#!/usr/bin/env python -# -# Collect and plot latency-profiling data from a running gpsd. -# Requires gnuplot. -# -import sys, os, time, getopt, tempfile, time, socket, math, copy -import gps - -class Baton: - "Ship progress indication to stderr." - def __init__(self, prompt, endmsg=None): - self.stream = sys.stderr - self.stream.write(prompt + "...") - if os.isatty(self.stream.fileno()): - self.stream.write(" \010") - self.stream.flush() - self.count = 0 - self.endmsg = endmsg - self.time = time.time() - return - - def twirl(self, ch=None): - if self.stream is None: - return - if ch: - self.stream.write(ch) - elif os.isatty(self.stream.fileno()): - self.stream.write("-/|\\"[self.count % 4]) - self.stream.write("\010") - self.count = self.count + 1 - self.stream.flush() - return - - def end(self, msg=None): - if msg == None: - msg = self.endmsg - if self.stream: - self.stream.write("...(%2.2f sec) %s.\n" % (time.time() - self.time, msg)) - return - -class spaceplot: - "Total times without instrumentation." - name = "space" - def __init__(self): - self.fixes = [] - def d(self, a, b): - return math.sqrt((a[0] - b[0])**2 + (a[1] - b[1])**2) - def gather(self, session): - # Include altitude, not used here, for 3D plot experiments. - # Watch out for the NaN value from gps.py. - self.fixes.append((session.fix.latitude, session.fix.longitude, session.fix.altitude)) - return True - def header(self, session): - res = "# Position uncertainty, %s, %s, %ds cycle\n" % \ - (title, session.gps_id, session.cycle) - return res - def data(self, session): - res = "" - for i in range(len(self.recentered)): - (lat, lon) = self.recentered[i][:2] - (raw1, raw2, alt) = self.fixes[i] - res += "%f\t%f\t%f\t%f\t%f\n" % (lat, lon, raw1, raw2, alt) - return res - def plot(self, title, session): - if len(self.fixes) == 0: - sys.stderr.write("No fixes collected, can't estimate accuracy.") - sys.exit(1) - # centroid is just arithmetic avg of lat,lon - self.centroid = (sum(map(lambda x:x[0], self.fixes))/len(self.fixes), sum(map(lambda x:x[1], self.fixes))/len(self.fixes)) - # Sort fixes by distance from centroid - self.fixes.sort(lambda x, y: cmp(self.d(self.centroid, x), self.d(self.centroid, y))) - # Convert fixes to offsets from centroid in meters - self.recentered = map(lambda fix: gps.MeterOffset(self.centroid, fix[:2]), self.fixes) - # Compute CEP(50%) - cep_meters = gps.EarthDistance(self.centroid[:2], self.fixes[len(self.fixes)/2][:2]) - alt_sum = 0 - alt_num = 0 - lon_max = -9999 - for i in range(len(self.recentered)): - (lat, lon) = self.recentered[i][:2] - (raw1, raw2, alt) = self.fixes[i] - if not gps.isnan(alt): - alt_sum += alt - alt_num += 1 - if lon > lon_max : - lon_max = lon - if alt_num == 0: - alt_avg = gps.NaN - else: - alt_avg = alt_sum / alt_num - if self.centroid[0] < 0: - latstring = "%fS" % -self.centroid[0] - elif self.centroid[0] == 0: - latstring = "0" - else: - latstring = "%fN" % self.centroid[0] - if self.centroid[1] < 0: - lonstring = "%fW" % -self.centroid[1] - elif self.centroid[1] == 0: - lonstring = "0" - else: - lonstring = "%fE" % self.centroid[1] - fmt = "set autoscale\n" - fmt += 'set key below\n' - fmt += 'set key title "%s"\n' % time.asctime() - fmt += 'set size ratio -1\n' - fmt += 'set style line 2 pt 1\n' - fmt += 'set style line 3 pt 2\n' - fmt += 'set xlabel "Meters east from %s"\n' % lonstring - fmt += 'set ylabel "Meters north from %s"\n' % latstring - fmt += 'set border 15\n' - if not gps.isnan(alt_avg): - fmt += 'set y2label "Meters Altitude from %f"\n' % alt_avg - fmt += 'set ytics nomirror\n' - fmt += 'set y2tics\n' - fmt += 'cep=%f\n' % self.d((0,0), self.recentered[len(self.fixes)/2]) - fmt += 'set parametric\n' - fmt += 'set trange [0:2*pi]\n' - fmt += 'cx(t, r) = sin(t)*r\n' - fmt += 'cy(t, r) = cos(t)*r\n' - fmt += 'chlen = cep/20\n' - fmt += "set arrow from -chlen,0 to chlen,0 nohead\n" - fmt += "set arrow from 0,-chlen to 0,chlen nohead\n" - fmt += 'plot cx(t, cep),cy(t, cep) title "CEP (50%%) = %f meters", ' % (cep_meters) - fmt += ' "-" using 1:2 with points ls 3 title "%d GPS fixes" ' % (len(self.fixes)) - if not gps.isnan(alt_avg): - fmt += ', "-" using ( %f ):($5 < 100000 ? $5 - %f : 1/0) axes x1y2 with points ls 2 title " %d Altitude fixes, Average = %f" \n' % (lon_max +1, alt_avg, alt_num, alt_avg) - else: - fmt += "\n" - fmt += self.header(session) - fmt += self.data(session) - if not gps.isnan(alt_avg): - fmt += "e\n" + self.data(session) - return fmt - -class uninstrumented: - "Total times without instrumentation." - name = "uninstrumented" - def __init__(self): - self.stats = [] - def gather(self, session): - if session.fix.time: - seconds = time.time() - session.fix.time - self.stats.append(seconds) - return True - else: - return False - def header(self, session): - return "# Uninstrumented total latency, %s, %s, %dN%d, cycle %ds\n" % \ - (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - def data(self, session): - res = "" - for seconds in self.stats: - res += "%2.6lf\n" % seconds - return res - def plot(self, title, session): - fmt = ''' -set autoscale -set key below -set key title "Uninstrumented total latency, %s, %s, %dN%d, cycle %ds" -plot "-" using 0:1 title "Total time" with impulses -''' - res = fmt % (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - res += self.header(session) - return res + self.data(session) - -class rawplot: - "All measurement, no deductions." - name = "raw" - def __init__(self): - self.stats = [] - def gather(self, session): - self.stats.append(copy.copy(session.timings)) - return True - def header(self, session): - res = "# Raw latency data, %s, %s, %dN%d, cycle %ds\n" % \ - (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - res += "# tag len xmit " - for hn in ("T1", "D1", "E2", "T2", "D2"): - res += "%-13s" % hn - res += "\n#------- ----- --------------------" - for i in range(0, 5): - res += " " + ("-" * 11) - return res + "\n" - def data(self, session): - res = "" - for timings in self.stats: - res += "% 8s %4d %2.9f %2.9f %2.9f %2.9f %2.9f %2.9f\n" \ - % (timings.tag, - timings.len, - timings.xmit, - timings.recv - timings.xmit, - timings.decode - timings.recv, - timings.emit - timings.decode, - timings.c_recv - timings.emit, - timings.c_decode - timings.c_recv) - return res - def plot(self, file, session): - fmt = ''' -set autoscale -set key below -set key title "Raw latency data, %s, %s, %dN%d, cycle %ds" -plot \ - "-" using 0:8 title "D2 = Client decode time" with impulses, \ - "-" using 0:7 title "T2 = TCP/IP latency" with impulses, \ - "-" using 0:6 title "E2 = Daemon encode time" with impulses, \ - "-" using 0:5 title "D1 = Daemon decode time" with impulses, \ - "-" using 0:4 title "T1 = RS232 time" with impulses -''' - res = fmt % (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - res += self.header(session) - for dummy in range(0, 5): - res += self.data(session) + "e\n" - return res - -class splitplot: - "Discard base time, use color to indicate different tags." - name = "split" - sentences = [] - def __init__(self): - self.stats = [] - def gather(self, session): - self.stats.append(copy.copy(session.timings)) - if session.timings.tag not in self.sentences: - self.sentences.append(session.timings.tag) - return True - def header(self, session): - res = "# Split latency data, %s, %s, %dN%d, cycle %ds\n#" % \ - (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - for s in splitplot.sentences: - res += "%8s\t" % s - for hn in ("T1", "D1", "E2", "T2", "D2", "length"): - res += "%8s\t" % hn - res += "tag\n# " - for s in tuple(splitplot.sentences) + ("T1", "D1", "E2", "T2", "D2", "length"): - res += "---------\t" - return res + "--------\n" - def data(self, session): - res = "" - for timings in self.stats: - for s in splitplot.sentences: - if s == timings.tag: - res += "%2.6f\t" % timings.xmit - else: - res += "- \t" - res += "%2.6f\t%2.6f\t%2.6f\t%2.6f\t%2.6f\t%8d\t# %s\n" \ - % (timings.recv - timings.xmit, - timings.decode - timings.recv, - timings.emit - timings.decode, - timings.c_recv - timings.emit, - timings.c_decode - timings.c_recv, - timings.len, - timings.tag) - return res - def plot(self, title, session): - fixed = ''' -set autoscale -set key below -set key title "Filtered latency data, %s, %s, %dN%d, cycle %ds" -plot \ - "-" using 0:%d title "D2 = Client decode time" with impulses, \ - "-" using 0:%d title "T2 = TCP/IP latency" with impulses, \ - "-" using 0:%d title "E2 = Daemon encode time" with impulses, \ - "-" using 0:%d title "D1 = Daemon decode time" with impulses, \ - "-" using 0:%d title "T1 = RS3232 time" with impulses, \ -''' - sc = len(splitplot.sentences) - fmt = fixed % (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle, - sc+5, - sc+4, - sc+3, - sc+2, - sc+1) - for i in range(sc): - fmt += ' "-" using 0:%d title "%s" with impulses,' % \ - (i+1, self.sentences[i]) - res = fmt[:-1] + "\n" - res += self.header(session) - for dummy in range(sc+5): - res += self.data(session) + "e\n" - return res - -class cycle: - "Send-cycle analysis." - name = "cycle" - def __init__(self): - self.stats = [] - def gather(self, session): - self.stats.append(copy.copy(session.timings)) - return True - def plot(self, title, session): - msg = "" - def roundoff(n): - # Round a time to hundredths of a second - return round(n*100) / 100.0 - intervals = {} - last_seen = {} - for timing in self.stats: - # Throw out everything but the leader in each GSV group - if timing.tag[-3:] == "GSV" and last_command[-3:] == "GSV": - continue - last_command = timing.tag - # Record timings - received = timing.d_received() - if not timing.tag in intervals: - intervals[timing.tag] = [] - if timing.tag in last_seen: - intervals[timing.tag].append(roundoff(received - last_seen[timing.tag])) - last_seen[timing.tag] = received - - # Step three: get command frequencies and the basic send cycle time - frequencies = {} - for (key, interval_list) in intervals.items(): - frequencies[key] = {} - for interval in interval_list: - frequencies[key][interval] = frequencies[key].get(interval, 0) + 1 - # filter out noise - for key in frequencies: - distribution = frequencies[key] - for interval in distribution.keys(): - if distribution[interval] < 2: - del distribution[interval] - cycles = {} - for key in frequencies: - distribution = frequencies[key] - if len(frequencies[key].values()) == 1: - # The value is uniqe after filtering - cycles[key] = distribution.keys()[0] - else: - # Compute the mode - maxfreq = 0 - for (interval, frequency) in distribution.items(): - if distribution[interval] > maxfreq: - cycles[key] = interval - maxfreq = distribution[interval] - msg += "Cycle report %s, %s, %dN%d, cycle %ds" % \ - (title, - session.gps_id, session.baudrate, - session.stopbits, session.cycle) - msg += "The sentence set emitted by this GPS is: %s\n" % " ".join(intervals.keys()) - for key in cycles: - if len(frequencies[key].values()) == 1: - if cycles[key] == 1: - msg += "%s: is emitted once a second.\n" % key - else: - msg += "%s: is emitted once every %d seconds.\n" % (key, cycles[key]) - else: - if cycles[key] == 1: - msg += "%s: is probably emitted once a second.\n" % key - else: - msg += "%s: is probably emitted once every %d seconds.\n" % (key, cycles[key]) - sendcycle = min(*cycles.values()) - if sendcycle == 1: - msg += "Send cycle is once per second.\n" - else: - msg += "Send cycle is once per %d seconds.\n" % sendcycle - return msg - -formatters = (spaceplot, uninstrumented, rawplot, splitplot, cycle) - -def plotframe(await, fname, speed, threshold, title): - "Return a string containing a GNUplot script " - if fname: - for formatter in formatters: - if formatter.name == fname: - plotter = formatter() - break - else: - sys.stderr.write("gpsprof: no such formatter.\n") - sys.exit(1) - try: - session = gps.gps(verbose=verbose) - except socket.error: - sys.stderr.write("gpsprof: gpsd unreachable.\n") - sys.exit(1) - # Initialize - session.poll() - if session.version == None: - print >>sys.stderr, "gpsprof: requires gpsd to speak new protocol." - sys.exit(1) - session.send("?DEVICES;") - while session.poll() != -1: - if session.data["class"] == "DEVICES": - break - if len(session.data.devices) != 1: - print >>sys.stderr, "gpsprof: exactly one device must be attached." - sys.exit(1) - device = session.data.devices[0] - path = device["path"] - session.baudrate = device["bps"] - session.parity = device["bps"] - session.stopbits = device["stopbits"] - session.cycle = device["cycle"] - session.gps_id = device["driver"] - # Set parameters - if speed: - session.send('?DEVICE={"path":"%s","bps:":%d}' % (path, speed)) - session.poll() - if session.baudrate != speed: - sys.stderr.write("gpsprof: baud rate change failed.\n") - options = "" - if formatter not in (spaceplot, uninstrumented): - options = ',"timing":true' - try: - #session.set_raw_hook(lambda x: sys.stderr.write(`x`+"\n")) - session.send('?WATCH={"enable":true%s}' % options) - baton = Baton("gpsprof: looking for fix", "done") - countdown = await - basetime = time.time() - while countdown > 0: - if session.poll() == -1: - sys.stderr.write("gpsprof: gpsd has vanished.\n") - sys.exit(1) - baton.twirl() - if session.data["class"] == "WATCH": - if "timing" in options and not session.data.get("timing"): - sys.stderr.write("gpsprof: timing is not enabled.\n") - sys.exit(1) - # We can get some funky artifacts at start of session - # apparently due to RS232 buffering effects. Ignore - # them. - if threshold and time.time()-basetime < session.cycle * threshold: - continue - if session.fix.mode <= gps.MODE_NO_FIX: - continue - if countdown == await: - sys.stderr.write("first fix in %.2fsec, gathering %d samples..." % (time.time()-basetime,await)) - if plotter.gather(session): - countdown -= 1 - baton.end() - finally: - session.send('?WATCH={"enable":false,"timing":false}') - command = plotter.plot(title, session) - del session - return command - -if __name__ == '__main__': - try: - (options, arguments) = getopt.getopt(sys.argv[1:], "f:hm:n:s:t:v") - formatter = "space" - raw = False - speed = 0 - title = time.ctime() - threshold = 0 - await = 100 - verbose = False - for (switch, val) in options: - if (switch == '-f'): - formatter = val - elif (switch == '-m'): - threshold = int(val) - elif (switch == '-n'): - await = int(val) - elif (switch == '-s'): - speed = int(val) - elif (switch == '-t'): - title = val - elif (switch == '-v'): - verbose = True - elif (switch == '-h'): - sys.stderr.write(\ - "usage: gpsprof [-h] [-m threshold] [-n samplecount] \n" - + "\t[-f {" + "|".join(map(lambda x: x.name, formatters)) + "}] [-s speed] [-t title]\n") - sys.exit(0) - sys.stdout.write(plotframe(await,formatter,speed,threshold,title)) - except KeyboardInterrupt: - pass - - |