1 <!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML
4.0//EN'
>
4 Copyright (C) 2006-2010 Jonathan Zarate
5 http://www.polarcloud.com/tomato/
7 For use with Tomato Firmware only.
8 No part of this file may be used without permission.
12 <meta http-equiv='content-type' content='text/html;charset=utf-
8'
>
13 <meta name='robots' content='noindex,nofollow'
>
14 <title>[<%
ident(); %>] Tools: Wireless Survey
</title>
15 <link rel='stylesheet' type='text/css' href='tomato.css'
>
16 <link rel='stylesheet' type='text/css' href='color.css'
>
17 <script type='text/javascript' src='tomato.js'
></script>
21 <style type='text/css'
>
37 border: 1px dashed
#f0f0f0;
51 <script type='text/javascript' src='debug.js'
></script>
53 <script type='text/javascript'
>
54 // <% nvram(''); %> // http_id
58 var dayOfWeek
= ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
60 Date
.prototype.toWHMS = function() {
61 return dayOfWeek
[this.getDay()] + ' ' + this.getHours() + ':' + this.getMinutes().pad(2)+ ':' + this.getSeconds().pad(2);
64 var sg
= new TomatoGrid();
66 sg
.sortCompare = function(a
, b
) {
67 var col
= this.sortColumn
;
68 var da
= a
.getRowData();
69 var db
= b
.getRowData();
74 r
= -cmpDate(da
.lastSeen
, db
.lastSeen
);
77 r
= cmpInt(da
.rssi
, db
.rssi
);
80 r
= cmpInt(da
.noise
, db
.noise
);
83 r
= cmpInt(da
.qual
, db
.qual
);
86 r
= cmpInt(da
.channel
, db
.channel
);
89 r
= cmpText(a
.cells
[col
].innerHTML
, b
.cells
[col
].innerHTML
);
91 if (r
== 0) r
= cmpText(da
.bssid
, db
.bssid
);
93 return this.sortAscending
? r
: -r
;
96 sg
.rateSorter = function(a
, b
)
103 sg
.populate = function()
105 var caps
= ['infra', 'adhoc', 'poll', 'pollreq', 'wep', 'shortpre', 'pbcc', 'agility', 'spectrum', null, 'shortslot', null, null, 'cck-ofdm'];
106 var ncaps
= [null, null /*40MHz*/, null, null, 'gf', 'sgi20', 'sgi40', 'stbc'];
107 var ncap
= '802.11n';
111 var i
, j
, k
, t
, e
, s
;
113 if ((wlscandata
.length
== 1) && (!wlscandata
[0][0])) {
114 setMsg("error: " + wlscandata
[0][1]);
118 for (i
= 0; i
< wlscandata
.length
; ++i
) {
122 for (j
= 0; j
< entries
.length
; ++j
) {
123 if (entries
[j
].bssid
== s
[0]) {
131 e
.firstSeen
= new Date();
134 e
.lastSeen
= new Date();
138 if (s
[7] != 0 && s
[9] != 0) {
139 e
.channel
= e
.channel
+ '<br><small>' + s
[9] + ' MHz</small>';
147 for (j
= 0; j
< caps
.length
; ++j
) {
148 if ((s
[3] & (1 << j
)) && (caps
[j
])) {
150 if (k
> cap_maxlen
) {
161 if (k
> cap_maxlen
) {
169 for (j
= 0; j
< ncaps
.length
; ++j
) {
170 if ((s
[8] & (1 << j
)) && (ncaps
[j
])) {
171 k
+= ncaps
[j
].length
;
172 if (k
> cap_maxlen
) {
186 for (j
= 0; j
< s
[6].length
; ++j
) {
188 var r
= (x
& 0x7F) / 2;
189 if (x
& 0x80) rb
.push(r
);
192 rb
.sort(this.rateSorter
);
193 rg
.sort(this.rateSorter
);
196 if (rb
.length
) t
= '<span class="brate">' + rb
.join(',') + '</span>';
198 if (rb
.length
) t
+= '<br>';
199 t
+='<span class="grate">' + rg
.join(',') + '</span>';
204 t
= E('expire-time').value
;
206 var cut
= (new Date()).getTime() - (t
* 1000);
207 for (i
= 0; i
< entries
.length
; ) {
208 if (entries
[i
].lastSeen
.getTime() < cut
) {
209 entries
.splice(i
, 1);
216 for (i
= 0; i
< entries
.length
; ++i
) {
222 e
.rssi
= MAX(e
.rssi
- 5, -101);
223 e
.noise
= MAX(e
.noise
- 2, -101);
224 if ((e
.rssi
== -101) || (e
.noise
== -101))
225 e
.noise
= e
.rssi
= -999;
229 e
.qual
= MAX(e
.rssi
- e
.noise
, 0);
231 seen
= e
.lastSeen
.toWHMS();
233 m
= Math
.floor(((new Date()).getTime() - e
.firstSeen
.getTime()) / 60000);
234 if (m
<= 10) seen
+= '<br> <b><small>NEW (' + -m
+ 'm)</small></b>';
238 if (mac
.match(/^(..):(..):(..)/))
239 mac
= '<a href="http://standards.ieee.org/cgi-bin/ouisearch?' + RegExp
.$1 + '-' + RegExp
.$2 + '-' + RegExp
.$3 + '" target="_new" title="OUI search">' + mac
+ '</a>';
242 '<small>' + seen
+ '</small>',
245 (e
.rssi
== -999) ? '' : (e
.rssi
+ ' <small>dBm</small>'),
246 (e
.noise
== -999) ? '' : (e
.noise
+ ' <small>dBm</small>'),
247 '<small>' + e
.qual
+ '</small> <img src="bar' + MIN(MAX(Math
.floor(e
.qual
/ 10), 1), 6) + '.gif">',
250 '' + e
.rates
], false);
254 if (useAjax()) s
= added
+ ' added, ' + removed
+ ' removed, ';
255 s
+= entries
.length
+ ' total.';
257 s
+= '<br><br><small>Last updated: ' + (new Date()).toWHMS() + '</small>';
263 sg
.setup = function() {
264 this.init('survey-grid', 'sort');
265 this.headerSet(['Last Seen', 'SSID', 'BSSID', 'RSSI ', 'Noise ', 'Quality', 'Ch', 'Capabilities', 'Rates']);
273 E('survey-msg').innerHTML
= msg
;
277 var ref
= new TomatoRefresh('update.cgi', 'exec=wlscan', 0, 'tools_survey_refresh');
279 ref
.refresh = function(text
)
294 if (!useAjax()) E('expire-time').style
.visibility
= 'hidden';
305 <body onload='init()'
>
306 <form action='javascript:{}'
>
307 <table id='container' cellspacing=
0>
308 <tr><td colspan=
2 id='header'
>
309 <div class='title'
>Tomato
</div>
310 <div class='version'
>Version <%
version(); %></div>
312 <tr id='body'
><td id='navi'
><script type='text/javascript'
>navi()</script></td>
314 <div id='ident'
><%
ident(); %></div>
318 <div class='section-title'
>Wireless Site Survey
</div>
319 <div class='section'
>
320 <table id='survey-grid' class='tomato-grid' cellspacing=
0></table>
321 <div id='survey-msg'
></div>
322 <div id='survey-controls'
>
323 <img src=
"spin.gif" id=
"refresh-spinner">
324 <script type='text/javascript'
>
325 genStdTimeList('expire-time', 'Auto Expire', 1);
326 genStdTimeList('refresh-time', 'Auto Refresh', 1);
328 <input type=
"button" value=
"Refresh" onclick=
"ref.toggle()" id=
"refresh-button">
332 <script type='text/javascript'
>
333 if ('<% wlclient(); %>' == '0') {
334 document
.write('<small>Warning: Wireless connections to this router may be disrupted while using this tool.</small>');
342 <tr><td id='footer' colspan=
2> </td></tr>
345 <script type='text/javascript'
>earlyInit();</script>