#!/usr/bin/perl # Andrew Daviel, April 2005 # convert NetStumbler NS1 binary data to Fig # e.g. ns12fig -v blah.ns1 710000 928000 49.2499 -123.2322 50000 > blah.fig # Usage: $0 <file.ns1> <xscale> [<yscale>] [<latoff>] [<longoff>] [<radius factor>] # radius factor makes points bigger; min. size is 1 # options: n - add text legend # e - show WEP encrypted/unencrypted # d - show all APs at same depth # v - verbose (cf. summary data) use Math::BigInt; # remember depths used - for merging files dbmopen(%depth,"ns1depthdb",0644) ; $verb = 0 ; $verb2 = 0 ; # show time # http://www.stumbler.net/ns1files.html # C uint8 S uint16 L uint32 l int32 Q uint64 d double # Flags (HEx) # 0001 ESS extended service set # 0002 IBSS ad hod/P2P # 0004 CF Pollable # 0008 CF-Poll request # 0010 WEP # 0020 Short Preamble # 0040 PBCC # 0080 Channel Agility # 0400 Short slot time # 2000 DSSS-OFDM $quiet = 0 ; $line_style = 0 ; $thickness = 1 ; $pen_color = 4 ; # red $red = 4 ; $gold = 31 ; $green = 14 ; $black = 0 ; $fill_color = 7 ; $pdepth = 40 ; $pen_style = -1 ; $area_fill = 20 ; $style_val = '0.000' ; $angle = '0.000' ; $textangle = '0.700' ; $pt = 2 ; # text point $pt = 18 ; # text point $join_style = 0 ; $cap_style = 0 ; $radius0 = 400000 ; $radius0 = 2000 ; $forward_arrow = 0 ; $backward_arrow = 0 ; $maxcol = 5 ; $legend2 = 0 ; while ($ARGV[0] =~ /-([nedv])/) { if ($1 eq 'n') { shift ; $legend = 1 ; } if ($1 eq 'e') { shift ; $doe = 1 ; } if ($1 eq 'd') { shift ; $dp1 = 1 ; } if ($1 eq 'v') { shift ; $verb = 1 ; } } $file = shift ; open (IN,$file) or die "Usage: $0 <file.ns1> <xscale> [<yscale>] [<latoff>] [<longoff>] [<radius factor>]\n" ; $xscale = shift ; unless ($xscale) { die "Usage: $0 <xscale> [<yscale>] [<xoff>] [<yoff>] [<radius factor>]\n" ; # e.g. 180000 } $yscale = shift ; unless ($yscale) { $yscale = $xscale ; } $yoff = shift ; $xoff = shift ; $radius0 = shift ; unless ($radius0) { $radius0 = 4000 ; } $radius = int ($xscale / $radius0) ; unless ($radius>0) { $radius = 1 ; } $scale2 = 1200/100 ; $scale2 = 1200 ; print<<EOT; #FIG 3.2 Landscape Flush left Inches C 100.00 Single -2 # generated by ns12fig with scale $xscale,$yscale offset $yoff,$xoff 1200 2 EOT read (IN,$in,4) ; unless ($in eq 'NetS') { die "$file has no NetStumbler magic\n" ; } read (IN,$in,4) ; $fv = unpack("L",$in) ; #print "File version $fv\n" ; read (IN,$in,4) ; $nap = unpack("L",$in) ; print STDERR "$nap APINFO entries\n" ; for ($ap=1;$ap<=$nap;$ap++) { # print "\nAP $ap\n" ; # print "\n" ; read (IN,$in,1) ; $ssidlen = unpack("C",$in) ; read (IN,$in,$ssidlen) ; $ssid = $in ; #print "SSID $ssid\t" ; read (IN,$in,6) ; @bssid = unpack("C6",$in) ; $bssid = sprintf("%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", $bssid[0],$bssid[1],$bssid[2],$bssid[3],$bssid[4],$bssid[5]) ; $ssid{$bssid} = $ssid ; read (IN,$in,4) ; $maxsig = unpack("l",$in) ; $maxsig{$bssid} = $maxsig ; read (IN,$in,4) ; $minns = unpack("l",$in) ; $minns{$bssid} = $minns ; read (IN,$in,4) ; $maxsnr = unpack("l",$in) ; $maxsnr{$bssid} = $maxsnr; #print "maxsig $maxsig min noise $minns max SNR $maxsnr\n" ; #print "\t$maxsig $minns $maxsnr" ; read (IN,$in,4) ; $flags = unpack("L",$in) ; $wep = $flags & 0x10 ; $fast = $flags & 0x2400 ; $hflags = sprintf("%4.4x", $flags) ; #printf ("\tflags %4.4x", $flags) ; read (IN,$in,4) ; $bint = unpack("L",$in) ; #print "\tb.int $bint" ; read (IN,$in,8) ; $first = filetotime($in) ; $ff = localtime($first) ; #print "\tfirst $ff\t" ; read (IN,$in,8) ; $last = filetotime($in) ; $dt = $last - $first ; $ff = localtime($last) ; #print "\tfor $dt sec\n" ; # http://blogs.msdn.com/oldnewthing/archive/2003/09/05/54806.aspx # http://maillist.perforce.com/pipermail/jamming/1997-August/000294.html read (IN,$in,8) ; $lat = unpack("d",$in) ; read (IN,$in,8) ; $long = unpack("d",$in) ; #printf("\tLL %.6f %.6f\n",$lat,$long) ; unless (!$lat and !$long or $lat == 0.0 and $long == 0.0 or $verb) { $x = $xscale * ($long - $xoff) / 180 ; $y = $yscale * ($lat - $yoff) / 180 ; # from worldmap.c if ($xmax eq '') { $xmax = $xmin = $x ; } if ($ymax eq '') { $ymax = $ymin = $y ; } if ($x>$xmax) { $xmax = $x ; } if ($y>$ymax) { $ymax = $y ; } if ($x<$xmin) { $xmin = $x ; } if ($y<$ymin) { $ymin = $y ; } $x = int($x * $scale2 + 0.5 ) ; $y = int($y * -$scale2 + 0.5 ) ; # from ps2fig $lat5 = sprintf("%.5f",$lat) ; $long5 = sprintf("%.5f",$long) ; print<<EOT; # Time $ff # Pos $lat5 $long5 # SSID $ssid $bssid # flags $hflags # sig $maxsig noise $minns SNR $maxsnr EOT } read (IN,$in,4) ; $ndata = unpack("L",$in) ; #print "$ndata data entries\n" ; for ($i=0;$i<$ndata;$i++) { read (IN,$in,8) ; if ($verb and $verb2) { $time = filetotime($in) ; $ltime = localtime($time) ; } read (IN,$in,4) ; $sig = unpack("l",$in) ; read (IN,$in,4) ; $noise = unpack("l",$in) ; read (IN,$in,4) ; $fix = unpack("l",$in) ; #if ($verb) { print "\t$ltime\t$sig\t$noise\t" ; } if ($fix) { read (IN,$in,8) ; $lat = unpack("d",$in) ; read (IN,$in,8) ; $long = unpack("d",$in) ; read (IN,$in,8) ; $alt = unpack("d",$in) ; read (IN,$in,4) ; $nsat = unpack("L",$in) ; read (IN,$in,8) ; $speed = unpack("d",$in) ; read (IN,$in,8) ; $track = unpack("d",$in) ; read (IN,$in,8) ; $mvar = unpack("d",$in) ; read (IN,$in,8) ; $hdop = unpack("d",$in) ; #print " LL $lat $long alt $alt Nsat $nsat speed $speed track $track Mvar $mvar hdop $hdop\n" ; $x = $xscale * ($long - $xoff) / 180 ; $y = $yscale * ($lat - $yoff) / 180 ; # from worldmap.c if ($xmax eq '') { $xmax = $xmin = $x ; } if ($ymax eq '') { $ymax = $ymin = $y ; } if ($x>$xmax) { $xmax = $x ; } if ($y>$ymax) { $ymax = $y ; } if ($x<$xmin) { $xmin = $x ; } if ($y<$ymin) { $ymin = $y ; } $x = int($x * $scale2 + 0.5 ) ; $y = int($y * -$scale2 + 0.5 ) ; # from ps2fig $x{$bssid} = $x ; $y{$bssid} = $y ; if ($verb) { #printf ("LL %.5f %.5f alt %.3f Nsat %i speed %.1f track %.1f mvar %.1f HDOP %i\n",$lat,$long, $alt, $nsat, $speed, $track, $mvar, $hdop) ; unless (!$lat and !$long or $lat == 0.0 and $long == 0.0) { $lat5 = sprintf("%.5f",$lat) ; $long5 = sprintf("%.5f",$long) ; print<<EOT; # Time $ff # Pos $lat $long # SSID $ssid $bssid # flags $hflags # sig $sig noise $noise EOT unless ($depth{$bssid}) { $depth{'MAX'}++ ; $depth{$bssid} = $depth{'MAX'} ; } unless ($dp1) { $pdepth = $depth{$bssid} ; } unless ($seen{$bssid}) { $seen{$bssid} = 1 ; if ($legend2) { print "4 0 $black $pdepth 0 0 $pt $textangle 0 210 750 $x $y $ssid\\001\n" ; } } $snr = $sig - $noise ; $pen_color = $red ; if ($sig == -32767) { $pen_color = $black ; } if ($snr > 5) { $pen_color = $gold ; } if ($snr > 10) { $pen_color = $green ; } $fill_color = $pen_color ; if ($doe) { $pen_color = $gold ; if ($wep) { $pen_color = $red ; } } $fill_color = $pen_color ; if (!$wep and !$doe or $doe and !$fast) { # circle $x2 = $x + $radius ; print "1 3 $line_style $thickness $pen_color" ; print " $fill_color $pdepth $pen_style $area_fill $angle 1 0.000 " ; print "$x $y $radius $radius $x $y $x2 $y \n" ; } else { # square $x1 = $x - $radius ; $x2 = $x + $radius ; $y1 = $y - $radius ; $y2 = $y + $radius ; #print "2 2 0 1 0 0 $depth 0 20 0.000 0 0 -1 0 0 5\n" ; print "2 2 $line_style $thickness $pen_color $fill_color $pdepth $pen_style $area_fill $angle 0 0 -1 0 0 5\n" ; print "\t$x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1\n" ; } } } } else { #if ($verb) { print "\n" ; } } } read (IN,$in,1) ; $len = unpack("C",$in) ; read (IN,$in,$len) ; $name = $in ; $name{$bssid} = $name ; if ($fv >= 8) { read (IN,$in,8) ; @chan = unpack("L2",$in) ; read (IN,$in,4) ; $lchan = unpack("L",$in) ; $lchan{$bssid} = $lchan ; read (IN,$in,4) ; $ip = unpack("L",$in) ; @ip = unpack("C4",$in) ; #printf ("channelbits %4.4x%4.4x",$chan[1], $chan[0]) ; #printf ("\tip %i.%i.%i.%i lastchan %i\n",$ip[0],$ip[1],$ip[2],$ip[3],$lchan) ; } if ($fv >= 11 ) { read (IN,$in,4) ; $minsig = unpack("l",$in) ; $minsig{$bssid} = $minsig ; read (IN,$in,4) ; $maxns = unpack("l",$in) ; $maxns{$bssid} = $maxns ; read (IN,$in,4) ; $maxrate = unpack("L",$in)/10 ; $maxrate{$bssid} = $maxrate ; read (IN,$in,4) ; $subnet = unpack("L",$in) ; @subnet = unpack("C4",$in) ; read (IN,$in,4) ; $mask = unpack("L",$in) ; @mask = unpack("C4",$in) ; #printf ("min sig %i max noise %i max rate %i subnet %i.%i.%i.%i mask %i.%i.%i.%i\n",$minsig,$maxns,$maxrate,$subnet[0],$subnet[1],$subnet[2],$subnet[3], $mask[0],$mask[1],$mask[2],$mask[3]) ; } if ($fv >= 12) { read (IN,$in,4) ; $miscflags = unpack("L",$in) ; #if ($miscflags) { printf ("misc flags %4.4x\n", $miscflags) ; } read (IN,$in,4) ; $ielength = unpack("L",$in) ; read (IN,$in,$ielength) ; @ie = unpack("C*",$in) ; #if ($ielength) { print "ie length $ielength\n" ; } } unless (!$lat and !$long or $lat == 0.0 and $long == 0.0 or $verb) { print<<EOT; # rate $maxrate # channel $lchan EOT unless ($depth{$bssid}) { $depth{'MAX'}++ ; $depth{$bssid} = $depth{'MAX'} ; } unless ($dp1) { $pdepth = $depth{$bssid} ; } unless ($seen{$bssid}) { $seen{$bssid} = 1 ; if ($legend2) { print "4 0 $black $pdepth 0 0 $pt $textangle 0 210 750 $x $y $ssid\\001\n" ; } } $pen_color = $red ; if ($snr > 5) { $pen_color = $gold ; } if ($snr > 10) { $pen_color = $green ; } $fill_color = $pen_color ; if ($doe) { $pen_color = $gold ; if ($wep) { $pen_color = $red ; } } $fill_color = $pen_color ; if (!$wep and !$doe or $doe and !$fast) { # circle $x2 = $x + $radius ; print "1 3 $line_style $thickness $pen_color" ; print " $fill_color $pdepth $pen_style $area_fill $angle 1 0.000 " ; print "$x $y $radius $radius $x $y $x2 $y \n" ; } else { # square $x1 = $x - $radius ; $x2 = $x + $radius ; $y1 = $y - $radius ; $y2 = $y + $radius ; #print "2 2 0 1 0 0 $depth 0 20 0.000 0 0 -1 0 0 5\n" ; print "2 2 $line_style $thickness $pen_color $fill_color $pdepth $pen_style $area_fill $angle 0 0 -1 0 0 5\n" ; print "\t$x1 $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1\n" ; } } } unless ($dp1) { print STDERR "Using ns1depthdb file for depth\n" ; foreach $bssid ( sort { $depth{$a} <=> $depth{$b} } keys %ssid) { print STDERR "Depth $depth{$bssid}\t$ssid{$bssid} $bssid $name{$bssid}\tCH $lchan{$bssid} maxsig $maxsig{$bssid} rate $maxrate{$bssid}\n" ; if ($legend) { print "4 0 $black $depth{$bssid} 0 0 $pt $textangle 0 210 750 $x{$bssid} $y{$bssid} $ssid{$bssid} $bssid $name{$bssid} CH $lchan{$bssid}\\001\n" ; } } } exit ; sub filetotime { my ($h,$l,$t,$r) ; ($l,$h) = unpack("L2",$_[0]) ; my $t = Math::BigInt->new($h); $t->blsft(32) ; $t->badd($l) ; $t->bdiv(10000000) ; # convert 100ns ticks to seconds $t->bsub("11644473600") ; # 134774 days = 369 years 1601 - 1970 return $t ; }