aredn/files/usr/local/bin/rssi_monitor

300 lines
8.7 KiB
Perl
Executable File

#!/usr/bin/perl
#
# GPL V2 or greater
# work around Atheros ANI overly attenuating recieve chain with tendency to become stuck
# Joe Ayers AE6XE ae6xe@arrl.net 2015-10-29
# A receive chain may go deaf at noise prone sites and some neighbors may drop out.
# The wireless driver poorly tunes and treats these neighbors as noise in error.
# This is a workaround until root cause driver updates occur.
$now=`cat /proc/uptime | cut -f1 -d" "`;
chomp $now;
exit 0 unless $now > 120;
$iface = "wlan0" ; # wireless interface
$datfile = "/tmp/rssi.dat";
$logfile = "/tmp/rssi.log";
open(my $lfh, '>>', $logfile) or die "Could not open file $logfile $!";
sub getRSSI
{
for (keys %rssi)
{
delete $rssi{$_};
}
open(FILE, "/usr/sbin/iw $iface station dump 2>&1 |") or die "/usr/sbin/iw failed $!";
$neighborCount = 0;
while($line = <FILE>)
{
if($line =~ /Station (\S+) \(on $iface\)/) { $mac = $1;}
if($antnum and $line =~ /signal:[ \t]+[-\d]+[ \t]*\[([-\d]+),[ \t]*([-\d]+)/)
{
$H = $1;
$V = $2;
}
if ((not $antnum) and $line =~ /signal:[ \t]+[-\d]+[ \t]*\[([-\d]+)\]/)
{
$H = $1;
}
if ($H)
{
if ($H < -95) { $rssi{$mac}{"Hrssi"}=-96 ; }
else { $rssi{$mac}{"Hrssi"}=$H ; }
undef $H;
$neighborCount += 1;
}
if ($V)
{
if ($V < -95) { $rssi{$mac}{"Vrssi"}=-96 ; }
else { $rssi{$mac}{"Vrssi"}=$V ; }
undef $V;
}
}
}
$antnum=`iw list | grep "Configured Antennas: TX" | cut -f6 -d" "`;
chomp $antnum;
if ($antnum eq "0x1")
{
$antnum=0;
}
else
{
$antnum=1; # more than one
}
if ( -e $datfile )
{
open(FILE, "<$datfile") or die "Unable to read \"$datfile\"";
while($line = <FILE>)
{
if ($antnum)
{
($mac, $aveH, $aveV, $stdDevH, $stdDevV, $numS, $last) = split /\|/, $line;
$rssiHist{$mac}{"aveV"} = $aveV;
$rssiHist{$mac}{"sdV"} = $stdDevV;
}
else
{
($mac, $aveH, $stdDevH, $numS, $last) = split /\|/, $line;
}
$rssiHist{$mac}{"aveH"} = $aveH;
$rssiHist{$mac}{"sdH"} = $stdDevH;
$rssiHist{$mac}{"num"} = $numS;
chomp $last;
$rssiHist{$mac}{"last"} = $last;
}
close FILE ;
}
$ofdm_level = `cat /sys/kernel/debug/ieee80211/phy0/ath9k/ani | grep "OFDM LEVEL" | cut -f2 -d: `;
$now=`cat /proc/uptime | cut -f1 -d" "`;
chomp $now;
getRSSI() ;
for (keys %rssi)
{
if ( $rssiHist{$_} and $now - $rssiHist{$_}{"last"} < 3600 )
{
$hit = 0 ;
$sdH3 = int(3 * $rssiHist{$_}{"sdH"} + .5);
# is the RSSI attenuated and 3 standard deviations away? Test is only 1 chain has dropped, not both.
if ($rssiHist{$_}{"aveH"} - $rssi{$_}{"Hrssi"} > $sdH3) { $hit += 1; }
if ( $antnum )
{
$sdV3 = int(3 * $rssiHist{$_}{"sdV"} + .5);
if ($rssiHist{$_}{"aveV"} - $rssi{$_}{"Vrssi"} > $sdV3) { $hit += 1; }
}
if ($rssiHist{$_}{"num"} > 9 and $ofdm_level <= 2 and $hit == 1)
{ # Overly Attenuated Chain Suspected
$datestring = localtime();
if ($antnum)
{
print $lfh "$datestring: Attenuated Suspect $_ [$rssi{$_}{'Hrssi'},$rssi{$_}{'Vrssi'}] $rssiHist{$_}{'aveH'} ";
print $lfh "$rssiHist{$_}{'aveV'} $rssiHist{$_}{'sdH'} $rssiHist{$_}{'sdV'}\n";
}
else
{
print $lfh "$datestring: Attenuated Suspect $_ [$rssi{$_}{'Hrssi'}] $rssiHist{$_}{'aveH'} $rssiHist{$_}{'sdH'}\n";
}
# find strongest signal to compare RSSI before/after reset
if ( $amac )
{
if ($antnum)
{
if ($rssi{$amac}{"Hrssi"} < $rssi{$amac}{"Vrssi"} ) { $strong1 = "Vrssi" ;} else {$strong1 = "Hrssi"; }
if ($rssi{$_}{"Hrssi"} < $rssi{$_}{"Vrssi"} ) { $strong2 = "Vrssi" ;} else {$strong2 = "Hrssi"; }
if ($rssi{$amac}{$strong1} < $rssi{$_}{$strong2} ) { $amac = $_ ;}
}
else
{
if ($rssi{$amac}{"Hrssi"} < $rssi{$_}{"Hrssi"} ) { $amac = $_ ;}
}
}
else { $amac = $_ ; }
next ; # do not update statistics when suspected condition
}
# unpdate statistics
$aveH = (($rssiHist{$_}{"aveH"}*$rssiHist{$_}{"num"})+$rssi{$_}{"Hrssi"}) / ($rssiHist{$_}{"num"} + 1 );
$sdH = sqrt((($rssiHist{$_}{"num"}-1)*($rssiHist{$_}{"sdH"}**2) +
(($rssi{$_}{"Hrssi"}-$aveH)*($rssi{$_}{"Hrssi"}-$rssiHist{$_}{"aveH"})))/$rssiHist{$_}{"num"});
chomp $aveH;
chomp $sdH;
if ($antnum)
{
$aveV = (($rssiHist{$_}{"aveV"}*$rssiHist{$_}{"num"})+$rssi{$_}{"Vrssi"}) / ($rssiHist{$_}{"num"} + 1 );
$sdV = sqrt((($rssiHist{$_}{"num"}-1)*($rssiHist{$_}{"sdV"}**2) +
(($rssi{$_}{"Vrssi"}-$aveV)*($rssi{$_}{"Vrssi"}-$rssiHist{$_}{"aveV"})))/$rssiHist{$_}{"num"});
chomp $aveV;
chomp $sdV;
}
$rssiHist{$_}{"aveH"} = $aveH;
$rssiHist{$_}{"sdH"} = $sdH;
$rssiHist{$_}{"last"} = $now;
if ($rssiHist{$_}{"num"} < 60 )
{
# keep statistics to 60 sample (minute) moving window
$rssiHist{$_}{"num"} += 1;
}
if ($antnum)
{
$rssiHist{$_}{"aveV"} = $aveV;
$rssiHist{$_}{"sdV"} = $sdV;
}
}
else
{ # new neigbor or data too old--restart history
$rssiHist{$_}{"aveH"} = $rssi{$_}{"Hrssi"};
$rssiHist{$_}{"sdH"} = 0;
$rssiHist{$_}{"num"} = 1;
$rssiHist{$_}{"last"} = $now;
if ($antnum)
{
$rssiHist{$_}{"aveV"} = $rssi{$_}{"Vrssi"};
$rssiHist{$_}{"sdV"} = 0;
}
}
}
if ($amac or not $neighborCount)
{
$chnum = `uci get wireless.radio0.channel`;
$chnum += 1;
if ($chnum == 8 or $chnum == 12 or $chnum == 100 or $chnum == 185) { $chnum -= 2; }
if ($chnum == 0) { $chnum = 1; }
$freq = `iw list | grep "\\\[$chnum\\\]" | head -1`;
$freq =~ /([\d]+)[ \t]+MHz[ \t]+/;
$freq = $1;
if ($amac)
{
$datestring = localtime();
if ($antnum) {print $lfh "$datestring: before $amac [ $rssi{$amac}{'Hrssi'}, $rssi{$amac}{'Vrssi'} ]\n";}
else {print $lfh "$datestring: before $amac [ $rssi{$amac}{'Hrssi'}]\n";}
}
system("/usr/sbin/iw $iface scan freq $freq passive > /dev/null");
if ($amac)
{
sleep 5;
$beforeH = $rssi{$amac}{"Hrssi"};
if ($antnum) { $beforeV = $rssi{$amac}{"Vrssi"}; }
getRSSI() ;
$datestring = localtime();
if ($antnum) {print $lfh "$datestring: after $amac [ $rssi{$amac}{'Hrssi'}, $rssi{$amac}{'Vrssi'} ]\n";}
else {print $lfh "$datestring: after $amac [ $rssi{$amac}{'Hrssi'}]\n";}
$falpos = 0;
if ($antnum)
{
if (abs ( $beforeH - $rssi{$amac}{"Hrssi"} ) <= 2 and
abs ( $beforeV - $rssi{$amac}{"Vrssi"} ) <= 2 ) { $falpos = 1; }
}
elsif (abs ( $beforeH - $rssi{$amac}{"Hrssi"} ) <= 2 ) { $falpos = 1; }
if ( $falpos )
{
# if a false-positive (within 2dB change after a reset), then add data point to statistics
$aveH = (($rssiHist{$amac}{"aveH"}*$rssiHist{$amac}{"num"})+ $beforeH )
/ ($rssiHist{$amac}{"num"} + 1 );
$sdH = sqrt((($rssiHist{$amac}{"num"}-1)*($rssiHist{$amac}{"sdH"}**2) +
(($beforeH-$aveH)*($beforeH-$rssiHist{$amac}{"aveH"})))
/$rssiHist{$amac}{"num"});
chomp $aveH;
chomp $sdH;
$rssiHist{$amac}{"aveH"} = $aveH;
$rssiHist{$amac}{"sdH"} = $sdH;
if ($antnum)
{
$aveV = (($rssiHist{$amac}{"aveV"}*$rssiHist{$amac}{"num"})+ $beforeV ) /
($rssiHist{$amac}{"num"} + 1 );
$sdV = sqrt((($rssiHist{$amac}{"num"}-1)*($rssiHist{$amac}{"sdV"}**2) +
(($beforeV-$aveV)*($beforeV-$rssiHist{$amac}{"aveV"}))) /
$rssiHist{$amac}{"num"});
chomp $aveV;
chomp $sdV;
$rssiHist{$amac}{"aveV"} = $aveV;
$rssiHist{$amac}{"sdV"} = $sdV;
}
if ($rssiHist{$amac}{"num"} < 60 )
{
# keep statistics to 60 sample (minute) moving window
$rssiHist{$amac}{"num"} += 1;
}
$rssiHist{$amac}{"last"} = $now + 5 ;
$datestring = localtime();
print $lfh "$datestring: $amac Possible valid data point, adding to statistics.\n";
}
}
}
close $lfh;
open($dfh, ">$datfile") or die "Unable to create \"$datfile\" $!";
for (keys %rssiHist)
{
if ($antnum)
{
print $dfh "$_|$rssiHist{$_}{'aveH'}|$rssiHist{$_}{'aveV'}|$rssiHist{$_}{'sdH'}|";
print $dfh "$rssiHist{$_}{'sdV'}|$rssiHist{$_}{'num'}|$rssiHist{$_}{'last'}\n";
}
else
{
print $dfh "$_|$rssiHist{$_}{'aveH'}|$rssiHist{$_}{'sdH'}|$rssiHist{$_}{'num'}|$rssiHist{$_}{'last'}\n";
}
}
close $dfh;
# when logfile gets 1k over $MAXSIZE, then chop down
$MAXSIZE = 2**14;
exit 0 unless -s $logfile > $MAXSIZE + 1024;
@ARGV = $logfile;
undef $/;
$^I = "";
while (<>)
{
substr($_, 0, length() - $MAXSIZE) = "";
s/.*\n//;
print;
}