wireless.htm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371
  1. <%#
  2. Copyright 2011 Jo-Philipp Wich <jow@openwrt.org>
  3. Licensed to the public under the Apache License 2.0.
  4. -%>
  5. <%-
  6. local ntm = require "luci.model.network".init()
  7. local dev
  8. local devices = { }
  9. for _, dev in luci.util.vspairs(luci.sys.net.devices()) do
  10. if dev:match("^wlan%d") or dev:match("^ath%d") or dev:match("^wl%d") then
  11. devices[#devices+1] = dev
  12. end
  13. end
  14. local curdev = luci.dispatcher.context.requestpath
  15. curdev = curdev[#curdev] ~= "wireless" and curdev[#curdev] or devices[1]
  16. -%>
  17. <%+header%>
  18. <script type="text/javascript" src="<%=resource%>/cbi.js"></script>
  19. <script type="text/javascript">//<![CDATA[
  20. var bwxhr = new XHR();
  21. var G, G2;
  22. var TIME = 0;
  23. var RATE = 1;
  24. var RSSI = 2;
  25. var NOISE = 3;
  26. var width = 760;
  27. var height = 300;
  28. var step = 5;
  29. var data_wanted = Math.floor(width / step);
  30. var data_fill = 0;
  31. var data_stamp = 0;
  32. var data_rssi = [ ];
  33. var data_noise = [ ];
  34. var data_rate = [ ];
  35. var line_rssi;
  36. var line_noise;
  37. var line_rate;
  38. var label_25, label_25_2;
  39. var label_50, label_50_2;
  40. var label_75, label_75_2;
  41. var label_rssi_cur;
  42. var label_rssi_avg;
  43. var label_rssi_peak;
  44. var label_noise_cur;
  45. var label_noise_avg;
  46. var label_noise_peak;
  47. var label_rate_cur;
  48. var label_rate_avg;
  49. var label_rate_peak;
  50. var label_scale;
  51. /* wait for SVG */
  52. window.setTimeout(
  53. function() {
  54. var svg = document.getElementById('iwsvg');
  55. var svg2 = document.getElementById('iwsvg2');
  56. try {
  57. G = svg.getSVGDocument
  58. ? svg.getSVGDocument() : svg.contentDocument;
  59. G2 = svg2.getSVGDocument
  60. ? svg2.getSVGDocument() : svg2.contentDocument;
  61. }
  62. catch(e) {
  63. G = document.embeds['iwsvg'].getSVGDocument();
  64. G2 = document.embeds['iwsvg2'].getSVGDocument();
  65. }
  66. if (!G || !G2)
  67. {
  68. window.setTimeout(arguments.callee, 1000);
  69. }
  70. else
  71. {
  72. /* find sizes */
  73. width = svg.offsetWidth - 2;
  74. height = svg.offsetHeight - 2;
  75. data_wanted = Math.ceil(width / step);
  76. /* prefill datasets */
  77. for (var i = 0; i < data_wanted; i++)
  78. {
  79. data_rssi[i] = 0;
  80. data_noise[i] = 0;
  81. data_rate[i] = 0;
  82. }
  83. /* find svg elements */
  84. line_rssi = G.getElementById('rssi');
  85. line_noise = G.getElementById('noise');
  86. line_rate = G2.getElementById('rate');
  87. label_25 = G.getElementById('label_25');
  88. label_50 = G.getElementById('label_50');
  89. label_75 = G.getElementById('label_75');
  90. label_25_2 = G2.getElementById('label_25');
  91. label_50_2 = G2.getElementById('label_50');
  92. label_75_2 = G2.getElementById('label_75');
  93. label_rssi_cur = document.getElementById('rssi_bw_cur');
  94. label_rssi_avg = document.getElementById('rssi_bw_avg');
  95. label_rssi_peak = document.getElementById('rssi_bw_peak');
  96. label_noise_cur = document.getElementById('noise_bw_cur');
  97. label_noise_avg = document.getElementById('noise_bw_avg');
  98. label_noise_peak = document.getElementById('noise_bw_peak');
  99. label_rate_cur = document.getElementById('rate_bw_cur');
  100. label_rate_avg = document.getElementById('rate_bw_avg');
  101. label_rate_peak = document.getElementById('rate_bw_peak');
  102. label_scale = document.getElementById('scale');
  103. label_scale_2 = document.getElementById('scale2');
  104. /* plot horizontal time interval lines */
  105. for (var i = width % (step * 60); i < width; i += step * 60)
  106. {
  107. var line = G.createElementNS('http://www.w3.org/2000/svg', 'line');
  108. line.setAttribute('x1', i);
  109. line.setAttribute('y1', 0);
  110. line.setAttribute('x2', i);
  111. line.setAttribute('y2', '100%');
  112. line.setAttribute('style', 'stroke:black;stroke-width:0.1');
  113. var text = G.createElementNS('http://www.w3.org/2000/svg', 'text');
  114. text.setAttribute('x', i + 5);
  115. text.setAttribute('y', 15);
  116. text.setAttribute('style', 'fill:#999999; font-size:9pt');
  117. text.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
  118. label_25.parentNode.appendChild(line);
  119. label_25.parentNode.appendChild(text);
  120. var line2 = G2.createElementNS('http://www.w3.org/2000/svg', 'line');
  121. line2.setAttribute('x1', i);
  122. line2.setAttribute('y1', 0);
  123. line2.setAttribute('x2', i);
  124. line2.setAttribute('y2', '100%');
  125. line2.setAttribute('style', 'stroke:black;stroke-width:0.1');
  126. var text2 = G2.createElementNS('http://www.w3.org/2000/svg', 'text');
  127. text2.setAttribute('x', i + 5);
  128. text2.setAttribute('y', 15);
  129. text2.setAttribute('style', 'fill:#999999; font-size:9pt');
  130. text2.appendChild(G.createTextNode(Math.round((width - i) / step / 60) + 'm'));
  131. label_25_2.parentNode.appendChild(line2);
  132. label_25_2.parentNode.appendChild(text2);
  133. }
  134. label_scale.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
  135. label_scale_2.innerHTML = String.format('<%:(%d minute window, %d second interval)%>', data_wanted / 60, 3);
  136. /* render datasets, start update interval */
  137. XHR.poll(3, '<%=build_url("admin/status/realtime/wireless_status", curdev)%>', null,
  138. function(x, data)
  139. {
  140. var noise_floor = 255;
  141. var rate_floor = 60000;
  142. for (var i = 0; i < data.length; i++) {
  143. noise_floor = Math.min(noise_floor, data[i][NOISE]);
  144. rate_floor = Math.min(rate_floor, data[i][RATE]);
  145. }
  146. noise_floor -= 5;
  147. var data_max = 0;
  148. var data_scale = 0;
  149. var data_max_2 = 0;
  150. var data_scale_2 = 0;
  151. var data_rssi_avg = 0;
  152. var data_noise_avg = 0;
  153. var data_rate_avg = 0;
  154. var data_rssi_peak = 0;
  155. var data_noise_peak = 0;
  156. var data_rate_peak = 0;
  157. for (var i = data_stamp ? 0 : 1; i < data.length; i++)
  158. {
  159. /* skip overlapping entries */
  160. if (data[i][TIME] <= data_stamp)
  161. continue;
  162. data_rssi.push(data[i][RSSI] - noise_floor);
  163. data_noise.push(data[i][NOISE] - noise_floor);
  164. data_rate.push(Math.floor(data[i][RATE] / 1000));
  165. }
  166. /* cut off outdated entries */
  167. data_rssi = data_rssi.slice(data_rssi.length - data_wanted, data_rssi.length);
  168. data_noise = data_noise.slice(data_noise.length - data_wanted, data_noise.length);
  169. data_rate = data_rate.slice(data_rate.length - data_wanted, data_rate.length);
  170. /* find peak */
  171. for (var i = 0; i < data_rssi.length; i++)
  172. {
  173. data_max = Math.max(data_max, data_rssi[i]);
  174. data_max_2 = Math.max(data_max_2, data_rate[i]);
  175. data_rssi_peak = Math.max(data_rssi_peak, data_rssi[i]);
  176. data_noise_peak = Math.max(data_noise_peak, data_noise[i]);
  177. data_rate_peak = Math.max(data_rate_peak, data_rate[i]);
  178. if (i > 0)
  179. {
  180. data_rssi_avg = (data_rssi_avg + data_rssi[i]) / 2;
  181. data_noise_avg = (data_noise_avg + data_noise[i]) / 2;
  182. data_rate_avg = (data_rate_avg + data_rate[i]) / 2;
  183. }
  184. else
  185. {
  186. data_rssi_avg = data_rssi[i];
  187. data_noise_avg = data_noise[i];
  188. data_rate_avg = data_rate[i];
  189. }
  190. }
  191. /* remember current timestamp, calculate horizontal scale */
  192. data_stamp = data[data.length-1][TIME];
  193. data_scale = (height / (data_max * 1.1)).toFixed(1);
  194. data_scale_2 = (height / (data_max_2 * 1.1)).toFixed(1);
  195. /* plot data */
  196. var pt_rssi = '0,' + height;
  197. var pt_noise = '0,' + height;
  198. var pt_rate = '0,' + height;
  199. var y_rssi = 0;
  200. var y_noise = 0;
  201. var y_rate = 0;
  202. for (var i = 0; i < data_rssi.length; i++)
  203. {
  204. var x = i * step;
  205. y_rssi = height - Math.floor(data_rssi[i] * data_scale);
  206. y_noise = height - Math.floor(data_noise[i] * data_scale);
  207. y_rate = height - Math.floor(data_rate[i] * data_scale_2);
  208. y_rssi -= Math.floor(y_rssi % (1/data_scale));
  209. y_noise -= Math.floor(y_noise % (1/data_scale));
  210. pt_rssi += ' ' + x + ',' + y_rssi;
  211. pt_noise += ' ' + x + ',' + y_noise;
  212. pt_rate += ' ' + x + ',' + y_rate;
  213. }
  214. pt_rssi += ' ' + width + ',' + y_rssi + ' ' + width + ',' + height;
  215. pt_noise += ' ' + width + ',' + y_noise + ' ' + width + ',' + height;
  216. pt_rate += ' ' + width + ',' + y_rate + ' ' + width + ',' + height;
  217. line_rssi.setAttribute('points', pt_rssi);
  218. line_noise.setAttribute('points', pt_noise);
  219. line_rate.setAttribute('points', pt_rate);
  220. function wireless_label(dbm, noise)
  221. {
  222. if (noise)
  223. return String.format("%d <%:dBm%> (SNR %d <%:dBm%>)", noise_floor + dbm - 255, dbm - noise);
  224. else
  225. return String.format("%d <%:dBm%>", noise_floor + dbm - 255);
  226. }
  227. function rate_label(mbit)
  228. {
  229. return String.format("%d <%:Mbit/s%>", mbit);
  230. }
  231. label_25.firstChild.data = wireless_label(1.1 * 0.25 * data_max);
  232. label_50.firstChild.data = wireless_label(1.1 * 0.50 * data_max);
  233. label_75.firstChild.data = wireless_label(1.1 * 0.75 * data_max);
  234. label_25_2.firstChild.data = rate_label(1.1 * 0.25 * data_max_2);
  235. label_50_2.firstChild.data = rate_label(1.1 * 0.50 * data_max_2);
  236. label_75_2.firstChild.data = rate_label(1.1 * 0.75 * data_max_2);
  237. label_rssi_cur.innerHTML = wireless_label(data_rssi[data_rssi.length-1], data_noise[data_noise.length-1]).nobr();
  238. label_noise_cur.innerHTML = wireless_label(data_noise[data_noise.length-1]).nobr();
  239. label_rssi_avg.innerHTML = wireless_label(data_rssi_avg, data_noise_avg).nobr();
  240. label_noise_avg.innerHTML = wireless_label(data_noise_avg).nobr();
  241. label_rssi_peak.innerHTML = wireless_label(data_rssi_peak, data_noise_peak).nobr();
  242. label_noise_peak.innerHTML = wireless_label(data_noise_peak).nobr();
  243. label_rate_cur.innerHTML = rate_label(data_rate[data_rate.length-1]);
  244. label_rate_avg.innerHTML = rate_label(data_rate_avg);
  245. label_rate_peak.innerHTML = rate_label(data_rate_peak);
  246. }
  247. );
  248. }
  249. }, 1000
  250. );
  251. //]]></script>
  252. <h2><a id="content" name="content"><%:Realtime Wireless%></a></h2>
  253. <ul class="cbi-tabmenu">
  254. <% for _, dev in ipairs(devices) do %>
  255. <li class="cbi-tab<%= dev == curdev and "" or "-disabled" %>"><a href="<%=pcdata(dev)%>"><%=pcdata(dev)%></a></li>
  256. <% end %>
  257. </ul>
  258. <embed id="iwsvg" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wireless.svg" />
  259. <div style="text-align:right"><small id="scale">-</small></div>
  260. <br />
  261. <table style="width:100%; table-layout:fixed" cellspacing="5">
  262. <tr>
  263. <td style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid blue"><%:Signal:%></strong></td>
  264. <td id="rssi_bw_cur">0 <%:dBm%></td>
  265. <td style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></td>
  266. <td id="rssi_bw_avg">0 <%:dBm%></td>
  267. <td style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></td>
  268. <td id="rssi_bw_peak">0 <%:dBm%></td>
  269. </tr>
  270. <tr>
  271. <td style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid red"><%:Noise:%></strong></td>
  272. <td id="noise_bw_cur">0 <%:dBm%></td>
  273. <td style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></td>
  274. <td id="noise_bw_avg">0 <%:dBm%></td>
  275. <td style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></td>
  276. <td id="noise_bw_peak">0 <%:dBm%></td>
  277. </tr>
  278. </table>
  279. <br />
  280. <embed id="iwsvg2" style="width:100%; height:300px; border:1px solid #000000; background-color:#FFFFFF" src="<%=resource%>/wifirate.svg" />
  281. <div style="text-align:right"><small id="scale2">-</small></div>
  282. <br />
  283. <table style="width:100%; table-layout:fixed" cellspacing="5">
  284. <tr>
  285. <td style="text-align:right; vertical-align:top"><strong style="border-bottom:2px solid green"><%:Phy Rate:%></strong></td>
  286. <td id="rate_bw_cur">0 MBit/s</td>
  287. <td style="text-align:right; vertical-align:top"><strong><%:Average:%></strong></td>
  288. <td id="rate_bw_avg">0 MBit/s</td>
  289. <td style="text-align:right; vertical-align:top"><strong><%:Peak:%></strong></td>
  290. <td id="rate_bw_peak">0 MBit/s</td>
  291. </tr>
  292. </table>
  293. <%+footer%>