#!/usr/bin/perl

#
# wpm: a Wireless Power Meter
#
# Hacked together quite quickly by Rob Flickenger, 4/10/02
#
# Use only in an ansi-capable terminal!
#

##
# Tuning parameters:
#
# Set $signalMAX to the number that qualifies as all green.  Generally, this 
# should be set to whatever level your radio needs to sync at 11Mbps.
# 
# Set $noiseMAX to the amount of noise that qualifies as all red (the lower
# the number, the less noise it takes to qualify as all red).
#
# These are both expressed in dBm.
#
my $signalMAX = 86;
my $noiseMAX = 70;

##
# Poll interval: how long to wait between iterations (can be decimal).
#
my $Poll = 0.2;

##
# $Margin is the fade margin, in dB.  This gets factored into the meter, to
# give you a better representation of your link budget.  Set to 0 to disable.
#
# Keep in mind that the numbers reported are always as measured; this just
# affects the signal meter.
#
my $Margin = 3;

##
# Change nothing below.  Unless you want to.
#
my $version = "0.00";

$|++;

sub position {
  my $x = shift;
  my $y = shift;

  return "[$y;$x" . "H";
}

my @labels = qw{ partner netname mode channel rate 
                 signal noise link best title margin };

my @anim = qw { | / - \ };

my %text = (
  CLEAR => "[2J",
  RESET => "[37m",
  BLACK => "[30m",

  partner => position(4,20)  . "[0;36mTest Partner:",
  netname => position(4,21)  . "[0;36mNetwork Name:",
  mode    => position(12,22) . "[0;36mMode:",
  channel => position(9,23)  . "[0;36mChannel:",
  rate    => position(8,24)  . "[0;36mBit Rate:",

  signal  => position(58,20) . "[0;32mSignal:",
  noise   => position(59,21) . "[0;31mNoise:",
  link    => position(60,23) . "[0;36mLink:",
  best    => position(53,22) . "[0;36mBest Signal:",

  margin  => $Margin ? (position(34,14) . "[0;36m([0;37mMargin: $Margin dBm[0;36m)") : "",

  title   => position(30,2) . "[1;37mWirelessPowerMeter [0;37mv$version",

  s0 => "[0;32m[29C                       ",
  s1 => "[0;32m[40CO",
  s2 => "[0;32m[39C(O)",
  s3 => "[0;32m[38C( O )",
  s4 => "[0;32m[37C( (O) )",
  s5 => "[0;32m[37C(((O)))",
  s6 => "[0;32m[35C( (((O))) )",
  s7 => "[0;32m[31C(  (( (((O))) ))  )",
  s8 => "[0;32m[29C(((((((((((O)))))))))))",

  n1 => "[0;31m[40CO",
  n2 => "[0;31m[39C(O)",
  n3 => "[0;31m[38C((O))",
  n4 => "[0;31m[37C(((O)))",
  n5 => "[0;31m[35C(((((O)))))",
  n6 => "[0;31m[33C(((((((O)))))))",
  n7 => "[0;31m[31C(((((((((O)))))))))",
  n8 => "[0;31m[29C(((((((((((O)))))))))))",
  
);

my %poss = (
  partner => position(19,20),
  netname => position(19,21),
  mode    => position(19,22),
  channel => position(19,23),
  rate    => position(19,24),

  signal  => position(67,20),
  noise   => position(67,21),
  link    => position(67,23),
  best    => position(67,22),

  meter   => position(0,12),

  anim    => position(41,24),
);

my %chan = (
  2.412 => "1",
  2.417 => "2",
  2.422 => "3",
  2.427 => "4",
  2.432 => "5",
  2.437 => "6",
  2.442 => "7",
  2.447 => "8",
  2.452 => "9",
  2.457 => "10",
  2.462 => "11",
  2.467 => "12",
  2.472 => "13",
  2.467 => "14",
);

my %cfg = ( );
$cfg{bestnum} = 99;

##
# Enough initialization.
#
print $text{CLEAR};

my($iw,$i,$signal,$noise) = 0;

while(1) {
  $i++;

##
# Grab all data from an external iwconfig
#
  $iw = `iwconfig 2> /dev/null`;

  $iw =~ /ESSID:"(\w+)/;
  $cfg{netname} = $1;

  $iw =~ /Mode:(\w+).*ency:(.....).*Point: (..:..:..:..:..:..)/;
  $cfg{mode} = $1;
  $cfg{channel} = $chan{$2} . " ($2 GHZ)";
  $cfg{partner} = $3;

  $iw =~ m|Rate:(.*Mb/s)|;
  $cfg{rate} = $1;

  $iw =~ /Signal level:-(\d+).*Noise level:-(\d+)/;
  $cfg{signal} = "[1;37m-$1 dBm";
  $cfg{signalnum} = $1;

  #
  # Save the best (lowest) signal...
  #
  ($cfg{bestnum} > $cfg{signalnum}) && ($cfg{bestnum} = $cfg{signalnum});
  $cfg{best} = "[0;37m-$cfg{bestnum} dBm";
  #
  # ...and adjust for link margin...
  #
  $cfg{signalnum} += $Margin;

  $cfg{noise} = "[1;37m-$2 dBm";
  $cfg{noisenum} = $2;

  $cfg{linknum} = ($2 - $1);
  ($cfg{linknum} < 0) && ($cfg{linknum} = 0);
  $cfg{link} = " $cfg{linknum} dBm";

  ##
  # Then print the labels, clear the data area, and print the data
  #
  for(@labels) {
    print $text{$_} . $poss{$_} . (" " x 14) . $poss{$_} . $text{RESET} . $cfg{$_};
  }

  ##
  # Calculate signal & noise bars
  #
  $signal = int( ((100 - $cfg{signalnum}) * 8) / (100 - $signalMAX));
  ($signal > 8) && ($signal = 8);
  ($signal < 0) && ($signal = 0);

  $noise = int( ((100 - $cfg{noisenum}) * 8) / (100 - $noiseMAX));
  ($noise > 8) && ($noise = 8);
  ($noise < 0) && ($noise = 0);

  print $poss{meter} .  $text{s0};
  print $poss{meter} .  $text{"s" . $signal};
  print $poss{meter} .  $text{"n" . $noise};

  print $poss{anim}, $anim[($i % scalar(@anim))];

  print position(1,1), $text{BLACK}, " ", position(1,1) . $text{RESET};
  select(undef,undef,undef,$Poll);
}

print position(0,25) . $text{RESET};
exit;

#
# Ende
#
