|
要开发各种环境中网络覆盖度的精确数据模型,无线现场测量是必不可少的。创建大型区域信号强度图的现有解决方案必须花费大量时间通过人工操作来标明信号点并记录信号强度。带有 Hard Disk Active Protection System (HDAPS) 加速度传感器的 ThinkPad 通过计算行进距离并测量信号强度能够自动完成大部分此类工作。本文将使用样例代码从加速度传感器数据流中提取步长特性,并提供绘制大型区域无线网络信号强度图的呈现算法。
要求
硬件
许多在 2003 年以后(包括 2003 年)制造的 IBM ThinkPad 都配有 HDAPS 硬件。如果不能确定您的 ThinkPad 是否支持 HDAPS,可以到 Lenovo 的 Web 站点中查看详细的技术信息。必须配有能够正常运行的 802.11 网卡或内置的 WiFi 硬件。
软件
需要使用对 HDAPS 有内核支持的 Linux® 发行版。HDAPS 驱动程序必须包含在内核中才能访问加速度传感器。最新的内核发行版,包括 Red Hat、Debian、Gentoo 和 Ubuntu 都包括 HDAPS 驱动程序。要获得如何开始使用 HDAPS 内核驱动程序的更多信息,请在 参考资料
部分中查阅与 HDAPS 相关的文章。
还需要来自 Perl 文件归档 CPAN 中的 Time::HiRes 模块才能在记录数据时提供次秒级 (subsecond) 计时控制。ImageMagick 也是必需的,因为呈现程序将使用 convert 和 composite 命令来生成最终的信号强度图。有关这些工具的链接,请参阅 参考资料。
注,本文中描述的技术和算法应当适用于配有内置加速度传感器的所有笔记本计算机。拥有 MacBook 或运行非 Linux 操作系统的 ThinkPad 的技术人员应当能够轻松地调整本文提供的代码。
用 recordData.pl 采集数据
recordData.pl 程序描述
清单 1 显示了 recordData.pl 程序。前 12 行定义程序的全局变量,包括查找 HDAPS 传感器数据的位置以及无线网络连接信息。子程序 readPosition 和 readSignal 将只从相应文件中提取 (x,y) 加速度传感器值和信号强度值。在本例中,主程序循环将输出这些信息并暂停二十分之一秒。根据数据采集环境,可以要求每秒采集更多样本或添加单独的参数,例如 ESSID。考虑到本文的目的,每秒 20 个样本足以提供记录普通用户行走特性的分辨力。
清单 1. recordData.pl —— 加速度传感器和信号强度数据采集
#!/usr/bin/perl -w
# recordData.pl - record accelerometer readings and wireless signal strength
# usage: recordData.pl | tee logFile
use strict;
use Time::HiRes qw( usleep );
$|=1; # non-buffered output
my $hdapsFN = "/sys/devices/platform/hdaps/position"; # hdaps data
my $netwkFN = "/proc/net/wireless"; # network data
my ($restX, $restY ) = ""; # default position of hdaps sensors
my $SLEEP_INTERVAL = 50000; # microseconds to pause between data reads
# setup the default position of the hdaps sensor values
($restX, $restY ) = readPosition();
while(1)
{
my ($currX, $currY) = readPosition();
$currX -= $restX; # adjust for rest data state
$currY -= $restY;
print Time::HiRes::time(), " ", readSignal(), " $currX $currY \n";
usleep($SLEEP_INTERVAL);
}#end main loop
# begin subroutines
sub readPosition
{
my ($posX, $posY) = "";
open(FH," $hdapsFN") or die "can't open hdaps file";
while(my $hdapsLine = <FH> )
{
$hdapsLine =~ s/(\(|\))//g; # remove parens
($posX, $posY) = split ",", $hdapsLine;
}# while hdaps line read
close(FH);
return( $posX, $posY );
}#readPosition
sub readSignal
{
my $sigLev = 0;
open(FH," $netwkFN") or die "can't open wireless network file";
while( my $networkLine = <FH> )
{
next unless( /eth/ );
(undef, undef, undef, $sigLev ) = split " ", $networkLine;
}# while read
close(FH);
return( $sigLev );
}#readSignal
|
recordData.pl 记录数据的用法
把网卡与访问点关联起来并用 perl recordData.pl | tee logFileN 命令启动 recordData.pl 程序。如何进一步处理 logFile 中的输出取决于以特定方式持拿 ThinkPad 的用户。例如,走向需要进行数据采集的起点,合上 ThinkPad,并像拿着书或提着公事包一样把它持拿在身体的某一侧。在此位置暂停至少两秒,然后开始沿线路或走廊走向数据采集的终点。记住要在数据采集运行的终点处暂停至少两秒,然后打开 ThinkPad 并用 Ctrl+C 停止 recordData.pl 程序。
记住要自然行走。使您持拿 ThinkPad 的手臂自由摆动。稍后将把这些典型摆动检测为表示步长的特性。为每条线路记录另一个日志文件,并且给这些日志文件提供一个顺序名称或者编号方式以简化后期处理。从相关的平移和旋转中分离行走动作是通过侦听数据运行前后的两秒暂停来完成的。确保通过停顿来打断数据运行,这样才能确保后期处理成功。清单 2 显示了 recordData.pl 程序的示例输出,其中带有一次典型行走过程的时间、信号强度及加速度传感器读数 X 和 Y。
清单 2. recordData.pl 的示例输出
...
1199973721.01845 179. -15 -140
1199973721.06944 181. -11 -141
1199973721.12043 181. -8 -139
1199973721.22242 183. -4 -139
...
|
从记录数据中提取步长
将典型数据采集运行的输出可视化是最适于由 kst 来完成的任务 —— kst 是用于绘制数据的 KDE 应用程序。虽然本文不要求,但是对于分析实时数据和记录数据来说,kst 是必不可少的。如果安装了 kst,则可以用 kst -y 2 -y 3 -y 4 logFile 命令生成如下所示的曲线图。
图 1. 使用 kst 将数据采集可视化
图 1 显示了关键点的注释和指示符。演示了必要的事件序列,显示数据记录的起点,然后把 ThinkPad 移到垂直的 “行走” 位置。初始 “休息” 状态被记录,然后记录了 “行走” 部分。然后再次显示休息时段,然后移回至 “标准” 配置来手动停止程序。
提取相关数据部分
通过 recordData 日志文件创建信号强度图的第一步是只分离相关数据部分。清单 3 显示了 detectEnds.pl 程序,该程序被设计为只打印数据日志文件的 rest/walking/rest 部分。
detectEnds.pl 将从 recordData.pl 程序读取每行 logFile 输入。如果有超过两秒(基于 50,000 微秒延迟的 40 个样本)的数据的 Y 加速度传感器读数小于 -100,则执行一次简单的平均偏差检查。如果平均偏差接近于零,则设置一个休息状态。在初始休息状态之后,将基于一组点的最高偏差预计行走条件。在设置行走条件后,检测到的第二次休息将终止 detectEnds.pl 程序,同时生成与初始 logFile 格式相同但是拥有相当有用的数据的 endFile。
清单 3. detectEnds.pl —— 只输出日志文件的相关部分
#!/usr/bin/perl -w
# detectEnds.pl - find rest/walking/rest section of recordData.pl output
# usage: cat logFile | detectEnds.pl > endFile
use strict;
my @dataArr = ();
my $maxDeviation = 1;
my $minimumData = 40; # 2 seconds at 20 samples/sec
my $lastMode = "start";
while(my $line = <STDIN> )
{
my( $time, $signal, $x, $y ) = split " ", $line;
# only process entries where unit is rotated properly
next if( $y > -100 );
push @dataArr, $y; # add to the data sample
# if at least N seconds or data has been recorded
next if( @dataArr <= $minimumData );
# zero deviation is near zero movement (rest), deviation 4 or more is walking
my $deviation = avgCheck(9) + avgCheck(19) + avgCheck(29) + avgCheck(39);
if( $deviation == 0 )
{
# first rest, before any other state
$lastMode = "rest" if( $lastMode eq "start" );
# terminate if post-walking rest detected
exit(0) if ( $lastMode eq "walking" );
}elsif( $deviation >= 4 )
{
# set walking mode on first rest detection
$lastMode = "walking" if( $lastMode eq "rest" );
}#check deviation
shift @dataArr; # maintain a minimumData size sample
print $line if( $lastMode eq "rest" || $lastMode eq "walking" );
}#while line in
# begin subroutines
sub avgCheck
{
# build and average of two seconds worth of data, return an integer of it's
# deviation from the center point
my $avgIndex = $_[0];
my $avgArr = 0;
for my $eachArr ( @dataArr ){ $avgArr += $eachArr };
$avgArr = $avgArr / ($minimumData+1);
return(1) if( abs( $dataArr[$avgIndex] - $avgArr ) > $maxDeviation );
return(0);
}# avgCheck
|
用 cat logFileN | perl detectEnds.pl > endFileN 命令创建一个独立的行走文件。endFile 现在只包含加速度传感器和信号强度的相关部分,而不包含与程序的开始和结束相关的无关平移和旋转。
注意,要有效呈现数据,需要使线路数据保持独立。例如,如果有三条线路,请考虑使用下面一行代码来帮助自动处理多个文件:
perl -e 'while($c<3){`cat logFile$c | perl detectEnds.pl > endFile$c`;$c++}'
|
用 findStep.pl 标识步长
现在当可以在 endFile 中获得相关数据部分后,处理指示步长将简单得多。考虑图 2,该图显示了 kst 处理完 endFile 中指示步长的程序输出。清单 4 将描述 findStep.pl 程序,该程序将生成用于构建图 2 所示图像的数据文件。
图 2. 通过指明步长提炼数据
清单 4. findStep.pl 程序
#!/usr/bin/perl -w
# findStep.pl - indicate peaks and nadirs (left and right steps) in an endFile
use strict;
die "usage: cat endFile | findStep.pl dataSz lowOrder > steps" unless @ARGV==2;
my $dataSz = $ARGV[0]; # number of samples to process for a wave
my $lowOrder = $ARGV[1]; # level of modulation that indicates a low order wave
my @lineArr = ();
# read the whole file in to set defaults and compute total data size
while( my $line = <STDIN> )
{
chomp($line);
push @lineArr, "default $line -60 -240 ";
}#while stdin
die "specify dataSz at most (totalSampleSize/2)-1" if ( (@lineArr/2)+1 < $dataSz );
my $lineNum = $dataSz; # skip the first N samples (known 'resting' mode)
for( $lineNum = $dataSz; $lineNum < (@lineArr-$dataSz); $lineNum++ )
{
my ( undef, $time, $signal, $x, $y ) = split " ", $lineArr[$lineNum];
# require a coffee bender before twitch=step
next unless ( highOrderWave($lineNum, $y) == 1);
if( findModulation( "peak", $lineNum, $y ) == (($dataSz*2)+1) )
{
$lineArr[$lineNum] =~ s/default/peak /i;
$lineArr[$lineNum] = "peak $time $signal $x $y $y -240";
}elsif( findModulation( "nadir", $lineNum, $y ) == (($dataSz*2)+1) )
{
$lineArr[$lineNum] =~ s/default/nadir /i;
$lineArr[$lineNum] = "nadir $time $signal $x $y -60 $y";
}#if peak or nadir detected
}#for each linearr
for my $lineItem( @lineArr ){ print "$lineItem\n" }
|
findStep.pl 要求有两个参数:
-
dataSz' —— 形成一个波浪的最少数据点数目
-
lowOrder' —— 调整为一个 “步长” 所必须达到的最小阈值
用这些变量进行实验可能是必要的,这取决于数据采集程序的速度或持拿 ThinkPad 的方式。修改 recordData 和 detectEnds 中的样本大小可能要求更改这些变量。在读入文件内容确定全部数据大小并设置默认值后,将处理行走部分中的每个数据点。系统将执行两个检查 —— 一个用于确定满足合格行走幅度的调整,另一个用于检测调整类型。如果检测到峰值或谷值,则使用字符串标识符来设置该特殊条目,并且在处理后打印输出所有条目。
清单 5. highOrderWave 子程序
sub highOrderWave
{
my( $startVal, $currY ) = @_;
my $avgSize = 0;
# for selected sample size before and after current position, build average
for( my $pos = ($startVal-$dataSz); $pos <= ($startVal+$dataSz); $pos++ )
{
my ( undef, undef, undef, undef, $checkY ) = split " ", $lineArr[$pos];
$avgSize += $checkY;
}#for each position dataSz before and after
$avgSize = $avgSize / (($dataSz*2)+1);
$avgSize = abs($avgSize - $currY);
return(0) if( $avgSize < $lowOrder );
return(1);
}#highOrderWave
|
清单 5 详细显示了 highOrderWave 中的幅度调整检查。实现另一个简单的平均值检查算法,highOrderWave 将简单地计算当前条目的数据样本中的所有条目的偏差。如果偏差大于 lowOrder 参数,函数将返回 true 并且在主程序逻辑中对峰值或谷值调整执行进一步处理。在兴奋的程序员执行键入时的休息状态,以及在行走动作开始时的重心转移期间,低位调整十分常见。按照如上所述增加 lowOrder 变量,从而只分离由 findModulation 子程序检测到的最明显的调整,如下所示:
清单 6. findModulation 子程序
sub findModulation
{
my( $peakType, $startVal, $currY ) = @_;
my $totalMatch = 0;
# for selected sample size before and after current position
for( my $pos = ($startVal-$dataSz); $pos <= ($startVal+$dataSz); $pos++ )
{
# stop checking if a previous modulation found nearby
return(0) if( $lineArr[$pos] =~ /$peakType/ );
my ( undef, undef, undef, undef, $checkY ) = split " ", $lineArr[$pos];
if( $peakType eq "peak" ){ $totalMatch++ if( $checkY <= $currY ); }
elsif( $peakType eq "nadir" ){ $totalMatch++ if( $checkY >= $currY ); }
}#for each position dataSz before and after
return( $totalMatch );
}#findModulation
|
假定有显示峰值和谷值震荡模式的数据点集,那么通过简单测量邻近值来提取最高点和最低点将相对简单。对于当前样本大小中的每个数据点,findModulation 将首先在同一个样本范围内查找先前定义的调整事件。此检查将阻止稳定状态中的调整点集中在一起,并阻止邻近点检测出现中断。在指定了 “峰值” 检查后,findModulation 将计算位于或在当前数据点下方的点数。对于谷值检查,则执行相反操作,findModulation 子程序将返回位于或在当前数据点上方的总点数。回想一下清单 4,比较 findModulation 的返回值与总样本大小。如果计数小于或等于样本大小,则指明峰值。反过来,如果计数大于或等于样本大小,则设置谷值。
用 cat endFileN | perl findStep.pl 3 5 > stepFileN 命令生成带有峰值和谷值步长信息的文件。dataSz 的值为 3,而 lowOrder 的值为 5,这非常适合作者的数据采集文件。stepFileN 将包含开始图像呈现过程必需的最终信息。
通过记录数据呈现信号强度
设计图选择,标识线路起点
包含分隔步长以及相应的无线信号信息的数据文件现在已经准备好。楼宇的平面示意图是在一组有用参考点上表示此数据的理想选择。虽然从设计图到手绘图表的所有图像都将正常工作,但是呈现程序将要求使用正规维度。本例使用的图形全都是矩形,把图像中的楼宇划分成多个等分。如果楼宇的形状不规则(例如,L 形楼层图),则可能需要把可进行图示的部分划分成与相应线路数据集相对应的规则部分。
考虑下面图 3 的第一部分,获得示例图表。注意白色背景和图像周围的边框。这些特性将使呈现和合成更高效。您需要识别此图像中的线路起点以提供给呈现程序。例如,如果在 The Gimp 中打开设计图的图像文件并把光标放到线路数据采集的起点上,则可以看到线路的开始位置是 44,30。为已经采集的每行数据准备一列起点。
rowRender.pl 程序和设计决定
在继续介绍 rowRender.pl 程序和更多示例之前,对此实现所选择的呈现方法有必要作一些说明。对 gd 包可能有一些 C 和 Perl API 限制。不管选择哪些选项或指定什么参数,似乎都不可能绘制阿尔法混合 (alpha-blended) 圆形。消除此限制的最简单方法是使用 ImageMagick 的 convert 命令在命令行中绘制所有地理图元。rowRender.pl 程序将简单地构建一长串要执行的 ImageMagick convert 命令并批量运行命令来生成所需输出。
清单 7. rowRender.pl 程序 —— 主逻辑
#!/usr/bin/perl -w
# rowRender.pl - draw alpha blended signal strength circles at each step
use strict;
die "usage: cat steps | rowRender.pl inImg outImg stX stY size" unless @ARGV==5;
my $alpha = 220; # alpha color blending for signal circles
my @steps = (); # signal measurements
my $cmd = "convert "; # beginning of ImageMagick command
my ($inFile, $outFile, $startX, $startY, $dotSize) = @ARGV; # input, output files
# pre-compute the number of steps
while(my $line = <STDIN>)
{
my( undef, undef, $signal, undef, undef) = split " ", $line;
push @steps, $signal if( $line =~ /(peak|nadir)/ );
}#while stdin
# assumes consistent width through entire image
my( undef, undef, $width ) = split " ", `identify $inFile`;
$width = substr($width, 0, index($width,"x"));
$width = $width - ($startX * 2); # right image border compensation
# set stepSize manually for blueprint images with irregular borders, an 'L'
# shaped building for example
my $stepSize = $width / @steps;
for( my $lineNum = 0; $lineNum < @steps; $lineNum++ )
{
$cmd .= qq{ -fill "rgba(} . computeColor($steps[$lineNum]) . qq{,$alpha)" };
$cmd .= qq( -draw 'circle $startX,$startY, );
$cmd .= ($startX + $dotSize) . "," . ($startY + $dotSize) . qq(' \\\n);
runCommand() if( $lineNum % 50 == 0 );
$startX += $stepSize;
}#for each step
runCommand();
|
rowRender.pl 要求诸如设计图或其它图表之类的输入图像和输出图像名称。startX 和 startY 参数是线路的的开始坐标(所有呈现都是从左到右),而 dotSize 参数是在每个步长处要绘制的阿尔法混合圆的直径。
将计算步长总数,而且 ImageMagick 识别程序用于确定要呈现的图像宽度。rowRender 假定每行将被完全横断并且步长被平均地分布到整张图像中。对于这些均匀间隔的步长,将根据信号强度绘制阿尔法混合圆。清单 8 中所示的 computeColor 将确定要显示哪个级别的颜色,而 runCommand 将在构建时执行命令。
清单 8. rowRender.pl 中的 computeColor 子程序
sub computeColor
{
# simplistic computation of custom color range based on signal levels
my $signal = $_[0];
my $color = qq(255,140,10); # default orange
if( $signal >= 197 && $signal < 207 )
{
$color = abs( $signal - 207 );
$color = $color * 8; # intervals of 10
$color = $color + 175; # starting at green 175
$color = qq(59,$color,10);
}elsif( $signal >= 189 && $signal < 197 )
{
$color = abs( $signal - 197 );
$color = $color * 18; # 10 intervals
$color = $color + 75; # starting at red 75
$color = qq($color,234,39);
}elsif( $signal >= 181 && $signal < 189 )
{
$color = abs( $signal - 189 );
$color = $color * 15; # 10 intervals
$color = $color + 124; # starting at green 124
$color = qq(255,$color,10);
}
return($color);
}#computeColor
sub runCommand
{
$cmd = `$cmd $inFile $outFile`;
$inFile = "$outFile"; # use output as input for multi-pass image writing
$cmd = qq( convert );
}#runCommand
|
如上所示,选择了四个与红色 (<181)、橙色 (181-188)、黄色 (189-196) 和绿色 (197-207) 相对应的任意信号强度范围(bucket)。颜色值将根据信号级别落入的颜色分类范围来选择。这些值几乎肯定需要根据第一个步长期间采集的信号强度数据进行更改。这里的颜色选择算法为呈现差异提供了一个简单示例。
呈现最终图像
构建一个带有用 cat steps | perl rowRender.pl bluePrint.png out.png 44 20 30 命令呈现的单行数据的图表。回想一下,初始 X,Y 坐标是通过 The Gimp 图像编辑器确定并为这个特定行提供数据采集的起点。对于多行数据,考虑构建一个带有如下所示的相关行起点的 X,Y 坐标的文件。
清单 9. 多行呈现的命令示例
cat steps0 |perl rowRender1.pl bluePrint.png out.png 20 44 30
cat steps1 |perl rowRender1.pl out.png out.png 20 74 30
cat steps2 |perl rowRender1.pl out.png out.png 20 99 30
..
cat stepsN |perl rowRender1.pl out.png done.png X Y dotSize
|
您可能需要尝试更改阿尔法混合值才能更轻松地查看背景图表。如果您已经创建了一个带有白色背景的设计图文件,则使用 ImageMagick 可以轻松地完成最终覆盖。清单 10 显示了如何使用转换和合成,从而把设计图的可视部分轻松覆盖到信号强度呈现区域。
清单 10. 用于最终图像处理的 ImageMagick 命令
convert -transparent "#FFFFFF" bluePrint.png transparent.png
composite -compose over transparent.png done.png finalImg.png
|
图 3 显示了呈现上面列出的步长的示例输出。
图 3. 呈现步长合成图
结束语和其他示例
本文中提供的技术和代码使您可以使用比现有的停止和单击方法更高效的方法测量无线信号强度。为楼宇的多个楼层或者巨大的开放空间(例如仓库和制造工厂)绘制地图几乎可以像走路一样快速完成。通过此信号强度信息,您将能够更高效地分布位置访问点来提高无线网络的速度和覆盖度。
要进行进一步研究,请考虑把步长检测代码与 gpsd 和 kismet 集成在一起,增强大面积网络绘制设置的分辨率。请充分利用无线网卡的功能并同时从多个访问点绘制信号强度。
下载 | 描述 | 名字 | 大小 | 下载方法 |
|---|
| 样例代码 | os-wirelesssitesurveyofficeSignalMaps0.1.zip | 17KB | HTTP |
|---|
参考资料 学习
获得产品和技术
- 从 CPAN 获取 Time::HiRes 模块。
- 要获得 HDAPS,请了解 Linux 内核 下载镜像 的更多信息。
- 了解 The Gimp 图像处理软件的更多信息并下载。可以获得适用于 Windows®、Mac OS X 和各种 UNIX® 版本的下载。
-
ImageMagick 是用于创建、编辑和合成位图图像的软件套件,它可用于 Windows、Mac OS X 和各种 UNIX 版本。
- 从 Perl.org 获得 Perl。
- 使用 IBM 试用软件 改进您的下一个开发项目,这些软件可以通过下载或从 DVD 中获得。
- 下载 IBM 产品评估版,并开始使用 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。
讨论
|