summaryrefslogtreecommitdiff
path: root/gpsprof
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2011-09-24 11:49:58 -0400
committerEric S. Raymond <esr@thyrsus.com>2011-09-24 11:49:58 -0400
commit19be03197eb34f31b36e1b6cdef4ab8f29de1e0b (patch)
treeed3cc1e5aa903a65a558e6c5d9b36067ccb93643 /gpsprof
parent57b904484bebab6622d991d2c163d417880a18d4 (diff)
downloadgpsd-19be03197eb34f31b36e1b6cdef4ab8f29de1e0b.tar.gz
Refactor gpsprof to put most plotting machinery in a common base class.
Diffstat (limited to 'gpsprof')
-rwxr-xr-xgpsprof291
1 files changed, 142 insertions, 149 deletions
diff --git a/gpsprof b/gpsprof
index 77a9efc6..9078d27d 100755
--- a/gpsprof
+++ b/gpsprof
@@ -41,30 +41,97 @@ class Baton:
self.stream.write("...(%2.2f sec) %s.\n" % (time.time() - self.time, msg))
return
-class spaceplot:
+class plotter:
+ "Generic class for gatherling and plotting sensor statistics."
+ def __init__(self):
+ self.fixes = []
+ self.start_time = int(time.time())
+ def whatami(self):
+ "How do we identify this poltting run?"
+ return "%s, %s, %d %dN%d, cycle %ds" % \
+ (gps.misc.isotime(self.start_time),
+ self.device['driver'], self.device['bps'],
+ 9 - self.device['stopbits'],
+ self.device['stopbits'], self.device['cycle'])
+ def collect(self, verbose):
+ "Collect data from the GPS."
+ try:
+ self.session = gps.gps(verbose=verbose)
+ except socket.error:
+ sys.stderr.write("gpsprof: gpsd unreachable.\n")
+ sys.exit(1)
+ # Initialize
+ self.session.read()
+ if self.session.version == None:
+ print >>sys.stderr, "gpsprof: requires gpsd to speak new protocol."
+ sys.exit(1)
+ # Set parameters
+ options = ""
+ if self.requires_time:
+ options = ',"timing":true'
+ try:
+ self.session.send('?WATCH={"enable":true,"json":true%s}' % options)
+ baton = Baton("gpsprof: looking for fix", "done")
+ countdown = await
+ basetime = time.time()
+ while countdown > 0:
+ if self.session.read() == -1:
+ sys.stderr.write("gpsprof: gpsd has vanished.\n")
+ sys.exit(1)
+ baton.twirl()
+ if self.session.data["class"] == "DEVICES":
+ if len(self.session.data["devices"]) != 1:
+ print >>sys.stderr, "exactly one device must be attached.\n"
+ sys.exit(1)
+ self.device = copy.copy(self.session.data["devices"][0])
+ #sys.stderr.write("found %s device @%sbps..." % (self.device["driver"], self.device["bps"]))
+ if self.session.data["class"] == "WATCH":
+ if "timing" in options and not self.session.data.get("timing"):
+ sys.stderr.write("timing is not enabled.\n")
+ sys.exit(1)
+ # Ignore everything but TPVs for the moment
+ if self.session.data["class"] != "TPV":
+ continue
+ # We can get some funky artifacts at start of self.session
+ # apparently due to RS232 buffering effects. Ignore
+ # them.
+ if threshold and time.time()-basetime < self.session.cycle * threshold:
+ continue
+ if self.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.sample():
+ countdown -= 1
+ baton.end()
+ finally:
+ self.session.send('?WATCH={"enable":false,"timing":false}')
+ def dump(self):
+ "Dump the raw data for post-analysis."
+ return self.header() + self.data()
+
+class spaceplot(plotter):
"Spatial scattergram of fixes."
name = "space"
+ requires_time = False
def __init__(self):
- self.fixes = []
+ plotter.__init__(self)
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.
+ def sample(self):
# Watch out for the NaN value from gps.py.
- self.fixes.append((session.fix.latitude, session.fix.longitude, session.fix.altitude))
+ self.fixes.append((self.session.fix.latitude, self.session.fix.longitude, self.session.fix.altitude))
return True
- def header(self, session, device):
- res = "# Position uncertainty, %s, %s, %ds cycle\n" % \
- (title, device["driver"], device["cycle"])
- return res
- def data(self, unused, empty):
+ def header(self):
+ return "# Position uncertainty, %s\n" % self.whatami()
+ def data(self):
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, session, device, empty):
+ def plot(self):
if len(self.fixes) == 0:
sys.stderr.write("No fixes collected, can't estimate accuracy.")
sys.exit(1)
@@ -143,178 +210,91 @@ class spaceplot:
fmt += ', cx(t, cep95),cy(t, cep95) title "CEP (95%%) = %f meters"' % (cep95_meters)
fmt += ', cx(t, cep99),cy(t, cep99) title "CEP (99%%) = %f meters"' % (cep99_meters)
fmt += "\n"
- fmt += self.header(session, device)
- fmt += self.data(session, device)
+ fmt += self.header()
+ fmt += self.data()
if not gps.isnan(alt_avg):
- fmt += "e\n" + self.data(session, device)
+ fmt += "e\n" + self.data()
return fmt
-class uninstrumented:
+class uninstrumented(plotter):
"Total times without instrumentation."
name = "uninstrumented"
+ requires_time = True
def __init__(self):
- self.stats = []
- def gather(self, session):
- if session.fix.time:
- seconds = time.time() - gps.misc.isotime(session.data.time)
- self.stats.append(seconds)
+ plotter.__init__(self)
+ def sample(self):
+ if self.session.fix.time:
+ seconds = time.time() - gps.misc.isotime(self.session.data.time)
+ self.fixes.append(seconds)
return True
else:
return False
- def whatami(self, unused, device):
- return "%s, %s, %d %dN%d, cycle %ds" % \
- (gps.misc.isotime(int(time.time())),
- device['driver'], device['bps'],
- 9 - device['stopbits'],
- device['stopbits'], device['cycle'])
- def header(self, session, device):
- return "# Uninstrumented total latency, " + self.whatami(session, device) + "\n"
- def data(self, unused, empty):
+ def header(self):
+ return "# Uninstrumented total latency, " + self.whatami() + "\n"
+ def data(self):
res = ""
- for seconds in self.stats:
+ for seconds in self.fixes:
res += "%2.6lf\n" % seconds
return res
- def plot(self, session, device, dump):
- res = ""
- if not dump:
- fmt = '''\
+ def plot(self):
+ fmt = '''\
set autoscale
set key below
-set title "%s"
set key title "Uninstrumented total latency"
plot "-" using 0:1 title "Total time" with impulses
'''
- res = fmt % self.whatami(session, device)
- res += self.header(session, device)
- return res + self.data(session, device)
+ return fmt + self.header() + self.data()
-class instrumented:
+class instrumented(plotter):
"All measurement, no deductions."
name = "instrumented"
+ requires_time = True
def __init__(self):
- self.stats = []
- def gather(self, session):
- if 'xmit_time' in session.data:
- self.stats.append((session.data['tag'],
- gps.misc.isotime(session.data['time']),
- session.data["cycle_count"],
- session.data['cycle_start'],
- session.data['xmit_time'],
+ plotter.__init__(self)
+ def sample(self):
+ if 'xmit_time' in self.session.data:
+ self.fixes.append((self.session.data['tag'],
+ gps.misc.isotime(self.session.data['time']),
+ self.session.data["cycle_count"],
+ self.session.data['cycle_start'],
+ self.session.data['xmit_time'],
time.time()))
return True
- def whatami(self, unused, device):
- return "%s, %s, %d %dN%d, cycle %ds" % \
- (gps.misc.isotime(int(time.time())),
- device['driver'], device['bps'],
- 9 - device['stopbits'],
- device['stopbits'], device['cycle'])
- def header(self, session, device):
- res = "# Analyzed latency, " + self.whatami(session, device) + "\n"
+ def header(self):
+ res = "# Analyzed latency, " + self.whatami() + "\n"
res += "#- Tag - ----- Fix time ----- - Chars - - Latency - -- RS232 -- --Analysis- --- Recv --\n"
return res
- def data(self, unused, device):
+ def data(self):
res = ""
- for (tag, time, chars, start, xmit, recv) in self.stats:
- rs232_time = (chars * 10.0) / device['bps']
+ for (tag, time, chars, start, xmit, recv) in self.fixes:
+ rs232_time = (chars * 10.0) / self.device['bps']
res += "% 8s %.9f %9u %.9f %.9f %.9f %.9f\n" % (tag, time, chars, start-time, rs232_time, xmit-time, recv-time)
return res
- def plot(self, session, device, dump):
- if dump:
- return self.header(session, device) + self.data(session, device)
- else:
- legends = (
- "Reception delta",
- "Analysis time",
- "RS232 time",
- "Fix latency",
- )
- fmt = '''\
+ def plot(self):
+ legends = (
+ "Reception delta",
+ "Analysis time",
+ "RS232 time",
+ "Fix latency",
+ )
+ fmt = '''\
set autoscale
set key title "Analyzed latency"
set key below
-set title "%s"
plot \\\n'''
- for (i, legend) in enumerate(legends):
- j = len(legends) - i + 3
- fmt += ' "-" using 0:%d title "%s" with impulses, \\\n' % (j, legend)
- fmt = fmt[:-4] + "\n"
- res = fmt % self.whatami(session, device)
- res += self.header(session, device)
- res += (self.data(session, device) + "e\n") * len(legends)
- return res
+ for (i, legend) in enumerate(legends):
+ j = len(legends) - i + 3
+ fmt += ' "-" using 0:%d title "%s" with impulses, \\\n' % (j, legend)
+ fmt = fmt[:-4] + "\n"
+ return fmt + self.header() + (self.data() + "e\n") * len(legends)
formatters = (spaceplot, uninstrumented, instrumented)
-def plotframe(await, fname, threshold, title, dump):
- "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.read()
- if session.version == None:
- print >>sys.stderr, "gpsprof: requires gpsd to speak new protocol."
- sys.exit(1)
- # Set parameters
- options = ""
- if formatter not in (spaceplot, uninstrumented):
- options = ',"timing":true'
- try:
- session.send('?WATCH={"enable":true,"json":true%s}' % options)
- baton = Baton("gpsprof: looking for fix", "done")
- countdown = await
- basetime = time.time()
- while countdown > 0:
- if session.read() == -1:
- sys.stderr.write("gpsprof: gpsd has vanished.\n")
- sys.exit(1)
- baton.twirl()
- if session.data["class"] == "DEVICES":
- if len(session.data["devices"]) != 1:
- print >>sys.stderr, "exactly one device must be attached.\n"
- sys.exit(1)
- device = copy.copy(session.data["devices"][0])
- #sys.stderr.write("found %s device @%sbps..." % (device["driver"], device["bps"]))
- if session.data["class"] == "WATCH":
- if "timing" in options and not session.data.get("timing"):
- sys.stderr.write("timing is not enabled.\n")
- sys.exit(1)
- # Ignore everything but TPVs for the moment
- if session.data["class"] != "TPV":
- continue
- # 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(session, device, dump)
- del session
- return command
-
if __name__ == '__main__':
try:
(options, arguments) = getopt.getopt(sys.argv[1:], "df:hm:n:s:t:T:D:")
- formatter = "space"
+ plotmode = "space"
raw = False
title = None
threshold = 0
@@ -324,7 +304,7 @@ if __name__ == '__main__':
dump = False
for (switch, val) in options:
if (switch == '-f'):
- formatter = val
+ plotmode = val
elif (switch == '-m'):
threshold = int(val)
elif (switch == '-n'):
@@ -342,10 +322,23 @@ if __name__ == '__main__':
"usage: gpsprof [-h] [-D debuglevel] [-m threshold] [-n samplecount] [-d]\n"
+ "\t[-f {" + "|".join(map(lambda x: x.name, formatters)) + "}] [-s speed] [-t title] [-T terminal]\n")
sys.exit(0)
- if terminal:
- sys.stdout.write("set terminal %s\n" % terminal)
- sys.stdout.write(plotframe(await,formatter,threshold,title,dump))
- if title:
+ if plotmode:
+ for formatter in formatters:
+ if formatter.name == plotmode:
+ plotter = formatter()
+ break
+ else:
+ sys.stderr.write("gpsprof: no such formatter.\n")
+ sys.exit(1)
+ plotter.collect(verbose)
+ if dump:
+ sys.stdout.write(plotter.dump())
+ else:
+ if terminal:
+ sys.stdout.write("set terminal %s\n" % terminal)
+ sys.stdout.write(plotter.plot())
+ if not title:
+ title = plotter.whatami()
sys.stdout.write("set title %s\n" % title)
except KeyboardInterrupt:
pass