From 81dec0a5a480df04f5a0355405ee6014ea07e080 Mon Sep 17 00:00:00 2001 From: Beat Bolli Date: Thu, 3 Mar 2011 23:55:40 +0100 Subject: webgps.py: use the HTML5 instead of SVG This also cleans up the TABs in the Python source. Signed-off-by: Eric S. Raymond --- contrib/webgps.py | 332 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 192 insertions(+), 140 deletions(-) (limited to 'contrib/webgps.py') diff --git a/contrib/webgps.py b/contrib/webgps.py index 1438f8c0..eecf8dcb 100755 --- a/contrib/webgps.py +++ b/contrib/webgps.py @@ -14,14 +14,13 @@ TRACKMAX = 1024 STALECOUNT = 10 DIAMETER = 200 -XYOFFSET = 10 def polartocart(el, az): radius = DIAMETER * (1 - el / 90.0) # * math.cos(Deg2Rad(float(el))) theta = Deg2Rad(float(az - 90)) return ( - int(radius * math.cos(theta) + 0.5) + DIAMETER + XYOFFSET, - int(radius * math.sin(theta) + 0.5) + DIAMETER + XYOFFSET + -int(radius * math.cos(theta) + 0.5), # switch sides for a skyview! + int(radius * math.sin(theta) + 0.5) ) @@ -40,12 +39,11 @@ class Track: self.posn.append(pos) if len(self.posn) > TRACKMAX: self.posn = self.posn[-TRACKMAX:] - #print self.prn, self.posn return 1 return 0 class SatTracks(gps): - '''gpsd client writing HTML and SVG output.''' + '''gpsd client writing HTML5 and output.''' def __init__(self): gps.__init__(self) @@ -54,144 +52,187 @@ class SatTracks(gps): self.statetimer = time.time() self.needsupdate = 0 - def html(self, svgfile): - self.fh.write(""" - + def html(self, fh, jsfile): + fh.write(""" - + -\t +\t +\t \tGPSD Satellite Positions and Readings -\t +\t +\t -\t +\t
\t\t \t\t\t\n") - - # SVG stuff - self.fh.write("\t\t\t\n\t\t\n" % svgfile) - - self.fh.write("\t
-\t\t\t\t +\t\t\t\t
\t\t\t\t\t -""") +""" % jsfile) sats = self.satellites[:] sats.sort(lambda a, b: a.PRN - b.PRN) for s in sats: - self.fh.write("\t\t\t\t\t\n" % ( + fh.write("\t\t\t\t\t\n" % ( s.PRN, s.elevation, s.azimuth, s.ss, s.used and 'Y' or 'N' )) - self.fh.write("\t\t\t\t
PRN:Elev:Azim:SNR:Used:
%d%d%d%d%s
%d%d%d%d%s
\n\t\t\t\t\n") - - def row(l, v): - self.fh.write("\t\t\t\t\t\n" % (l, v)) - - def deg_to_str(a, hemi): - return '%.6f %c' % (abs(a), hemi[a < 0]) - - row('Time', self.utc or 'N/A') - - if self.fix.mode >= MODE_2D: - row('Latitude', deg_to_str(self.fix.latitude, 'SN')) - row('Longitude', deg_to_str(self.fix.longitude, 'WE')) - row('Altitude', self.fix.mode == MODE_3D and "%f m" % self.fix.altitude or 'N/A') - row('Speed', not isnan(self.fix.speed) and "%f m/s" % self.fix.speed or 'N/A') - row('Course', not isnan(self.fix.track) and "%f°" % self.fix.track or 'N/A') - else: - row('Latitude', 'N/A') - row('Longitude', 'N/A') - row('Altitude', 'N/A') - row('Speed', 'N/A') - row('Course', 'N/A') - - row('EPX', not isnan(self.fix.epx) and "%f m" % self.fix.epx or 'N/A') - row('EPY', not isnan(self.fix.epy) and "%f m" % self.fix.epy or 'N/A') - row('EPV', not isnan(self.fix.epv) and "%f m" % self.fix.epv or 'N/A') - row('Climb', self.fix.mode == MODE_3D and not isnan(self.fix.climb) and - "%f m/s" % self.fix.climb or 'N/A' - ) - - if not (self.valid & ONLINE_SET): - newstate = 0 - state = "OFFLINE" - else: - newstate = self.fix.mode - if newstate == MODE_2D: - state = self.status == STATUS_DGPS_FIX and "2D DIFF FIX" or "2D FIX" - elif newstate == MODE_3D: - state = self.status == STATUS_DGPS_FIX and "3D DIFF FIX" or "3D FIX" - else: - state = "NO FIX" - if newstate != self.state: - self.statetimer = time.time() - self.state = newstate - row('State', state + " (%d secs)" % (time.time() - self.statetimer)) - - self.fh.write("\t\t\t\t
%s:%s
\n\t\t\t
\n\t\t\t\t\n\ -\t\t\t
\n\n\n") - - def svg(self): - self.fh.write(""" - - -\t -\t\t -\t\t -\t\t -\t\t -\t\t -\t\t -\t\t -\t\t -\t\t\tN -\t\t\tW -\t\t\tE -\t\t\tS -\t\t -""" + fh.write("\t\t\t\t\n\t\t\t\t\n") + + def row(l, v): + fh.write("\t\t\t\t\t\n" % (l, v)) + + def deg_to_str(a, hemi): + return '%.6f %c' % (abs(a), hemi[a < 0]) + + row('Time', self.utc or 'N/A') + + if self.fix.mode >= MODE_2D: + row('Latitude', deg_to_str(self.fix.latitude, 'SN')) + row('Longitude', deg_to_str(self.fix.longitude, 'WE')) + row('Altitude', self.fix.mode == MODE_3D and "%f m" % self.fix.altitude or 'N/A') + row('Speed', not isnan(self.fix.speed) and "%f m/s" % self.fix.speed or 'N/A') + row('Course', not isnan(self.fix.track) and "%f°" % self.fix.track or 'N/A') + else: + row('Latitude', 'N/A') + row('Longitude', 'N/A') + row('Altitude', 'N/A') + row('Speed', 'N/A') + row('Course', 'N/A') + + row('EPX', not isnan(self.fix.epx) and "%f m" % self.fix.epx or 'N/A') + row('EPY', not isnan(self.fix.epy) and "%f m" % self.fix.epy or 'N/A') + row('EPV', not isnan(self.fix.epv) and "%f m" % self.fix.epv or 'N/A') + row('Climb', self.fix.mode == MODE_3D and not isnan(self.fix.climb) and + "%f m/s" % self.fix.climb or 'N/A' ) + if not (self.valid & ONLINE_SET): + newstate = 0 + state = "OFFLINE" + else: + newstate = self.fix.mode + if newstate == MODE_2D: + state = self.status == STATUS_DGPS_FIX and "2D DIFF FIX" or "2D FIX" + elif newstate == MODE_3D: + state = self.status == STATUS_DGPS_FIX and "3D DIFF FIX" or "3D FIX" + else: + state = "NO FIX" + if newstate != self.state: + self.statetimer = time.time() + self.state = newstate + row('State', state + " (%d secs)" % (time.time() - self.statetimer)) + + fh.write("""\t\t\t\t
%s:%s
+\t\t\t +\t\t\t +\t\t\t\t +\t\t\t\t\t

Your browser needs HTML5 <canvas> support to display the satellite view correctly.

+\t\t\t\t
+\t\t\t\t +\t\t\t +\t\t +\t + + +""") + + def js(self, fh): + fh.write("""// draw the satellite view + +function draw_satview() { + var c = document.getElementById('satview'); + if (!c.getContext) return; + var ctx = c.getContext('2d'); + if (!ctx) return; + + var circle = Math.PI * 2, + M = function (x, y) { ctx.moveTo(x, y); }, + L = function (x, y) { ctx.lineTo(x, y); }; + + ctx.save(); + ctx.clearRect(0, 0, c.width, c.height); + ctx.translate(210, 210); + + // grid and labels + ctx.strokeStyle = 'black'; + ctx.beginPath(); + ctx.arc(0, 0, 200, 0, circle, 0); + ctx.stroke(); + + ctx.beginPath(); + ctx.strokeText('N', -4, -202); + ctx.strokeText('E', -210, 4); + ctx.strokeText('W', 202, 4); + ctx.strokeText('S', -4, 210); + + ctx.strokeStyle = 'grey'; + ctx.beginPath(); + ctx.arc(0, 0, 100, 0, circle, 0); + M(2, 0); + ctx.arc(0, 0, 2, 0, circle, 0); + ctx.stroke(); + + ctx.strokeStyle = 'lightgrey'; + ctx.save(); + ctx.beginPath(); + M(0, -200); L(0, 200); ctx.rotate(circle / 8); + M(0, -200); L(0, 200); ctx.rotate(circle / 8); + M(0, -200); L(0, 200); ctx.rotate(circle / 8); + M(0, -200); L(0, 200); + ctx.stroke(); + ctx.restore(); + + // tracks + ctx.lineWidth = 0.6; + ctx.strokeStyle = 'red'; +"""); + + def M(p): + return 'M(%d,%d); ' % p + def L(p): + return 'L(%d,%d); ' % p + # Draw the tracks - self.fh.write('\t\t\n') for t in self.sattrack.values(): if t.posn: - self.fh.write('\t\t\t\n' % ( - ' '.join(['%d,%d' % p for p in t.posn]), t.stale == 0 and ' opacity=".33"' or '' + fh.write(" ctx.globalAlpha = %s; ctx.beginPath(); %s%sctx.stroke();\n" % ( + t.stale == 0 and '0.66' or '1', M(t.posn[0]), + ''.join([L(p) for p in t.posn[1:]]) )) - self.fh.write('\t\t\n') + + fh.write(""" + // satellites + ctx.lineWidth = 1; + ctx.strokeStyle = 'black'; +""") # Draw the satellites - self.fh.write('\t\t\n') for s in self.satellites: x, y = polartocart(s.elevation, s.azimuth) - fill = s.ss < 30 and 'red' or s.ss < 35 and 'yellow' or s.ss < 40 and 'green' or 'lime' - opaque = not s.used and ' opacity=".33"' or '' + fill = not s.used and 'lightgrey' or \ + s.ss < 30 and 'red' or \ + s.ss < 35 and 'yellow' or \ + s.ss < 40 and 'green' or 'lime' # Center PRNs in the marker offset = s.PRN < 10 and 3 or s.PRN >= 100 and -3 or 0 - if s.PRN > 32: # draw a diamond for SBAS satellites - self.fh.write( - '\t\t\t\n' % - (x + 8, y, fill, opaque) - ) + fh.write(" ctx.beginPath(); ctx.fillStyle = '%s'; " % fill) + if s.PRN > 32: # Draw a square for SBAS satellites + fh.write("ctx.rect(%d, %d, 16, 16); " % (x - 8, y - 8)) else: - self.fh.write( - '\t\t\t\n' % - (x, y, fill, opaque) - ) - self.fh.write('\t\t\t%d\n' % (x - 6 + offset, y + 4, s.PRN)) + fh.write("ctx.arc(%d, %d, 8, 0, circle, 0); " % (x, y)) + fh.write("ctx.fill(); ctx.stroke(); ctx.strokeText('%s', %d, %d);\n" % (s.PRN, x - 6 + offset, y + 4)) - self.fh.write('\t\t\n\t\n\n') + fh.write(""" + ctx.restore(); +} +""") def make_stale(self): for t in self.sattrack.values(): @@ -219,40 +260,51 @@ width=\"425\" height=\"425\" type=\"image/svg+xml\" />\n\ self.needsupdate = 1 self.delete_stale() - def generate_html(self, htmlfile, svgfile): - self.fh = open(htmlfile, 'w') - self.html(svgfile) - self.fh.close() - - def generate_svg(self, svgfile): - self.fh = open(svgfile, 'w') - self.svg() - self.fh.close() - - def run(self, period): - end = time.time() + period - self.stream(WATCH_ENABLE | WATCH_NEWSTYLE) - for report in self: - if report['class'] not in ('TPV', 'SKY'): - continue - self.needsupdate = 0 - self.update_tracks() - self.generate_html('gpsd.html', 'gpsd.svg') - if self.needsupdate: - self.generate_svg('gpsd.svg') - if period <= 0 and not isnan(self.fix.time): - break - if period > 0 and time.time() > end: - break + def generate_html(self, htmlfile, jsfile): + fh = open(htmlfile, 'w') + self.html(fh, jsfile) + fh.close() + + def generate_js(self, jsfile): + fh = open(jsfile, 'w') + self.js(fh) + fh.close() + + def run(self, suffix, period): + jsfile = 'gpsd' + suffix + '.js' + htmlfile = 'gpsd' + suffix + '.html' + end = time.time() + period + self.needsupdate = 1 + self.stream(WATCH_ENABLE | WATCH_NEWSTYLE) + for report in self: + if report['class'] not in ('TPV', 'SKY'): + continue + self.update_tracks() + if self.needsupdate: + self.generate_js(jsfile) + self.needsupdate = 0 + self.generate_html(htmlfile, jsfile) + if period <= 0 and self.fix.mode >= MODE_2D \ + or period > 0 and time.time() > end: + break def main(): argv = sys.argv[1:] - period = argv and argv[0] or '0' - if period[-1:] in 'smhd': - period = int(period[:-1]) * {'s': 1, 'm': 60, 'h': 60*60, 'd': 24*60*60}[period[-1]] + factors = { + 's': 1, 'm': 60, 'h': 60 * 60, 'd': 24 * 60 * 60 + } + arg = argv and argv[0] or '' + if arg[-1:] in factors.keys(): + period = int(arg[:-1]) * factors[arg[-1]] + elif arg == 'c': + period = None + elif arg: + period = int(arg) else: - period = int(period) + period = 0 + if arg: + arg = '-' + arg sat = SatTracks() @@ -264,7 +316,7 @@ def main(): p.close() try: - sat.run(period) + sat.run(arg, period) except KeyboardInterrupt: # save the tracks p = open(pfile, 'w') -- cgit v1.2.1