Quozl's OLPC XO-1 Signal Strength Meter |
| quozl@us.netrek.org
| up |
|
The levels are shown as circles of varying diameter, where the radius is determined by the level.
Figure 1 |
Figure 2 |
#!/usr/bin/python """ Signal Strength Meter Copyright (C) 2010 James Cameron (quozl@laptop.org) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ import sys, pygame license = [ "Signal Strength Meter", "Copyright (C) 2010 James Cameron <quozl@laptop.org>", " ", "This program comes with ABSOLUTELY NO WARRANTY;", "for details see source.", " ", "This is free software, and you are welcome to ", "redistribute it under certain conditions; see ", "source for details.", " " ] version = '0.3' pause = 100 # milliseconds between screen updates license_show_time = 10 # number of seconds to show license for thickness = 4 # thickness of circles in pixels freeze = False fonts = ('DejaVuSansMono.ttf', 'DejaVuLGCSansMono.ttf') fontpaths = ('/usr/share/fonts/dejavu/', '/usr/share/fonts/truetype/ttf-dejavu/') # colours c_black = (0, 0, 0) c_blue = (0, 0, 192) c_green = (0, 192, 0) c_red = (192, 0, 0) c_license = c_black c_link = c_blue c_signal = c_green c_noise = c_red def get_rf_raw(): """ obtain wireless status from first network interface """ fp = open('/proc/net/wireless', 'r') head = fp.readline() head = fp.readline() line = fp.readline() fp.close() (x, x, link, signal, noise, x, x, x, x, x, x) = line.split() return (int(link.replace('.', ' ')), int(signal.replace('.', ' ')), int(noise.replace('.', ' '))) samples = [] def get_rf(): global samples, min_l, min_s, min_n, max_l, max_s, max_n, avg_l, avg_s, avg_n raw = get_rf_raw() samples.append(raw) q = len(samples) if q > 100: samples = samples[1:] q -= 1 (l, s, n) = raw (min_l, min_s, min_n) = (l, s, n) (max_l, max_s, max_n) = (l, s, n) (tot_l, tot_s, tot_n) = (0, 0, 0) for sample in samples: (l, s, n) = sample (min_l, min_s, min_n) = (min(min_l, l), max(min_s, s), max(min_n, n)) (max_l, max_s, max_n) = (max(max_l, l), min(max_s, s), min(max_n, n)) tot_l += l tot_s += s tot_n += n avg_l = tot_l / q avg_s = tot_s / q avg_n = tot_n / q return raw # return (avg_l, avg_s, avg_n) # what to do when user wants to leave def op_quit(): pygame.display.quit() pygame.quit() sys.exit() # produce a screenshot for documentation purposes def op_copy(): pygame.image.save(screen, "/tmp/ssm-snapshot.png") print "snapshot taken" # toggle pause of screen updates def op_freeze(): global freeze freeze = not freeze def op_clear(): global samples samples = [] # control keyboard table, relates keys to functions kb_table_control = { pygame.K_d: op_quit, # control/d to quit pygame.K_c: op_copy, # control/c to make screen snapshot } # normal keyboard table, relates keys to functions kb_table_normal = { pygame.K_q: op_quit, # q to quit pygame.K_f: op_freeze, # f to freeze pygame.K_SPACE: op_freeze, # space to freeze pygame.K_BACKSPACE: op_clear, # backspace to clear samples } def kb(event): """ handle keyboard events from user """ # ignore the shift and control keys if event.key == pygame.K_LSHIFT or event.key == pygame.K_RSHIFT: return if event.key == pygame.K_LCTRL or event.key == pygame.K_RCTRL: return # check for control key sequences pressed if (event.mod & pygame.KMOD_CTRL): if kb_table_control.has_key(event.key): handler = kb_table_control[event.key] handler() return # check for normal keys pressed if kb_table_normal.has_key(event.key): handler = kb_table_normal[event.key] handler() return class FontCache: def __init__(self): self.cache = {} def read(self, names, size): if names == None: return pygame.font.Font(None, size) for name in names: for path in fontpaths: try: return pygame.font.Font(path + name, size) except: continue return pygame.font.Font(None, size) def get(self, names, size): key = (names, size) if key not in self.cache: self.cache[key] = self.read(names, size) return self.cache[key] def draw_license(): c = min(255 * (100 - license_show_count) / 100, 255) fn = fc.get(fonts, 34) x = 50 y = 394 for line in license: ts = fn.render(line, 1, (c, c, c), bg) tr = ts.get_rect(left=x, top=y) y = tr.bottom dirty.append(screen.blit(ts, tr)) def draw_labels(): fr = 40 # framing pad for data labels fn = fc.get(fonts, 35) ts = fn.render('ssm.py ' + version, 1, c_black, bg) tr = ts.get_rect(left=0, top=0) dirty.append(screen.blit(ts, tr)) ts = fn.render('LINK QUALITY', 1, c_link, bg) tr = ts.get_rect(centerx=width/2, top=fr) dirty.append(screen.blit(ts, tr)) ts = fn.render('SIGNAL LEVEL', 1, c_signal, bg) tr = ts.get_rect(left=fr, bottom=height-fr) dirty.append(screen.blit(ts, tr)) ts = fn.render('NOISE LEVEL', 1, c_noise, bg) tr = ts.get_rect(right=width-fr, bottom=height-fr) dirty.append(screen.blit(ts, tr)) fn = fc.get(fonts, 22) ts = fn.render('press q to quit', 1, c_black, bg) tr = ts.get_rect(centerx=width/2, bottom=height) y = tr.top dirty.append(screen.blit(ts, tr)) ts = fn.render('press space to pause', 1, c_black, bg) tr = ts.get_rect(centerx=width/2, bottom=y) dirty.append(screen.blit(ts, tr)) def draw_results(show): colour = c_black if not show: colour = bg fn = fc.get(fonts, 20) ts = fn.render('min link %3.0f signal %3.0f noise %3.0f' % (min_l, min_s, min_n), 1, colour, bg) tr = ts.get_rect(right=width-10, top=10) x = tr.left y = tr.bottom dirty.append(screen.blit(ts, tr)) ts = fn.render('avg link %3.0f signal %3.0f noise %3.0f' % (avg_l, avg_s, avg_n), 1, colour, bg) tr = ts.get_rect(left=x, top=y) x = tr.left y = tr.bottom dirty.append(screen.blit(ts, tr)) ts = fn.render('max link %3.0f signal %3.0f noise %3.0f' % (max_l, max_s, max_n), 1, colour, bg) tr = ts.get_rect(left=x, top=y) x = tr.left y = tr.bottom dirty.append(screen.blit(ts, tr)) ts = fn.render(' last %d samples' % len(samples), 1, colour, bg) tr = ts.get_rect(right=width-10, top=y+10) x = tr.left y = tr.bottom dirty.append(screen.blit(ts, tr)) def draw_item(show, colour, x, y, radius, v): if not show: colour = bg if radius < 0: radius = 0 p = pygame.draw.circle(screen, colour, (x, y), radius+thickness, thickness) fn = fc.get(fonts, 50) ts = fn.render(v, 1, colour, bg) tr = ts.get_rect(centerx=x, centery=y) q = screen.blit(ts, tr) dirty.append(pygame.Rect.union(p, q)) def draw_link(link, show): draw_item(show, c_link, width/2, height/3, link*15/10, "%d %%" % link) def draw_signal(signal, show): if signal != -256: # scanning? draw_item(show, c_signal, width/3, height*2/3, 156+signal, "%d dBm" % signal) def draw_noise(noise, show): draw_item(show, c_noise, width*2/3, height*2/3, 156+noise, "%d dBm" % noise) from optparse import OptionParser parser = OptionParser(usage="usage: %prog [options] message", version="%prog " + version) parser.add_option("--verbose", action="store_true", dest="verbose", default=False, help="generate verbose output") parser.add_option("--no-license", action="store_true", dest="no_license", default=False, help="do not display license") (opt, args) = parser.parse_args() if not opt.no_license: for line in license: print line pygame.init() fc = FontCache() screen = pygame.display.set_mode() width, height = screen.get_size() pygame.mouse.set_visible(False) white = (255, 255, 255) black = (0, 0, 0) pygame.time.set_timer(pygame.USEREVENT, pause) if opt.no_license: license_show_time = 0 license_show_count = (1000 / pause) * license_show_time fg = black bg = white # prepare background screen.fill(bg) pygame.display.flip() # initial drawing pass (link, signal, noise) = get_rf() dirty = [] draw_labels() draw_link(link, True) draw_signal(signal, True) draw_noise(noise, True) pygame.display.update(dirty) # main event loop while True: event = pygame.event.wait() if event.type == pygame.QUIT: op_quit() elif event.type == pygame.KEYDOWN: kb(event) elif event.type != pygame.USEREVENT: continue if freeze: continue dirty = [] if license_show_count > 0: # slow screen redraw with license present # everything is redrawn license_show_count = license_show_count - 1 draw_link(link, False) draw_signal(signal, False) draw_noise(noise, False) draw_results(False) if license_show_count == 0: dirty.append(screen.fill(bg)) else: draw_license() (link, signal, noise) = get_rf() draw_labels() draw_link(link, True) draw_signal(signal, True) draw_noise(noise, True) draw_results(True) else: # fast screen redraw with license absent # only changes are redrawn (old_link, old_signal, old_noise) = (link, signal, noise) (link, signal, noise) = get_rf() if signal == -256: # scanning? signal = old_signal draw_results(False) if link != old_link: draw_link(old_link, False) draw_link(link, True) if signal != old_signal: draw_signal(old_signal, False) draw_signal(signal, True) if noise != old_noise: draw_noise(old_noise, False) draw_noise(noise, True) draw_results(True) # update only the portions of screen that were changed pygame.display.update(dirty)