Ищем свободные Wi-Fi каналы с помощью GnuPlot

В эпоху интернета беспроводные Wi-Fi маршрутизаторы уже давно стали незаменимой частью нашего бытия. Wi-Fi сети работают в диапазоне 2,4 GHz либо 5 GHz и могут взаимно мешать работе друг друга например в ситуации, когда у вашего соседа тоже стоит роутер и работает на такой же частоте что и ваш. Для таких случаев в настройках роутера предусмотрена сетка каналов 1-13 (2.412GHz-2.472GHz). Соседей много, всем нужен хороший интернет. В данном материале мы научимся рисовать вот такую красивую картинку без стороннего ПО, т.е. исключительно средствами Linux и программы для построения графиков gnuplot.

TL;DR

screenshot

~$ sudo iwlist wlo1 scanning | python3 -c '

import re,sys

def tokenize(code):
    token_specification = [
        ("CELL",  r"^\s+Cell \d+"),
        ("FREQ",  r"^\s+Frequency:(\d+(\.\d*)?)"),
        ("CHNL",  r"^\s+Channel:(\d+)"),
        ("ESSID", r"^\s+ESSID:\"([- A-Za-z0-9$@^`,|%;.~()/\\{}:?[\]=+_#!]*)\""),
        ("LEVEL", r"\s+Signal level=(-\d+)"),
    ]
    tok_regex = "|".join("(?P<%s>%s)" % pair for pair in token_specification)

    for mo in re.finditer(tok_regex, code, re.M):
        typ = mo.lastgroup
        if typ == "CELL":
            if "res" in vars() and res:
                yield res
            res = {}
        else:
            grs = [v for v in mo.groups() if v not in [mo.group(typ), None]]
            assert len(grs), "Forgot capture value for %s ?" % typ
            res[typ] = grs[0]
    if res:
        yield res

data = list(tokenize(sys.stdin.read()))

print(
"""
# legend
set key top left outside horizontal
set xlabel "Channel"
set ylabel "Strength (dbm)"
# x axis step
set xtics 1
# show horizontal grid lines 
set grid ytics
set xrange [-2:16]
set yrange [%(bottom)d - 0.1:%(top)d]
set term png truecolor size 1280,720
plot for [IDX=0:%(total)d-1] "-" \
    using 2:(%(bottom)d):(4):(2*abs(%(bottom)d - $1)) \
    title columnheader(1) with ellipses \
    fill transparent solid 0.5 
""" 
% {
    "total": len(data), 
    "top": max([int(tok["LEVEL"]) for tok in data]) + 1, 
    "bottom": min([int(tok["LEVEL"]) for tok in data]) - 5, 
})

for tok in data:
    print("\"%s\"" % (tok["ESSID"] or "<HIDDEN>"))
    print(tok["LEVEL"], tok["CHNL"])
    print("e")
    assert set(("FREQ","CHNL","ESSID", "LEVEL")) <= set(tok)
' | gnuplot > out.png

Часто полезно видеть график в реальном времени while sleep 10; do sudo iwlist wlo1 scanning | python3 wifi.plot.py | gnuplot > out.png; done и какой нибудь периодический просмотрщик изображений типа feh --reload 1 out.png

GNUPLOT

В gnuplot можно рисовать графики как с помощью функций так и по заданным точкам. Нас интересует последний вариант:

~$ gnuplot --version
gnuplot 4.6 patchlevel 6
~$ echo '
plot for [IDX=0:1] "-" using 1:2 title columnheader(1) with lines
hello
1 1
3 3
e
world
1 3
3 1
e
' | gnuplot -p

screenshot

~$ echo '
set term png truecolor size 1280,480
set xrange [0:6]
plot for [IDX=0:3] "-" using 2:1 title columnheader(1) with points pointsize 3
"Anna"
-93 1
e
"Erika"
-73 1
e
"Alina"
-84 1
e
"Nast"
-93 5
e
' | gnuplot > wifi.points.png

screenshot

~$ echo '
set term png truecolor size 1280,480
set xlabel "Channel"
set ylabel "Strength (dbm)"
set xrange [0:6]
set yrange [-110:-60]
set grid ytics
plot for [IDX=0:3] "-" using 2:1:(2):(abs($1/3)) title columnheader(1) with ellipses
"Anna"
-93 1
e
"Erika"
-73 1
e
"Alina"
-84 1
e
"Nast"
-93 5
e
' | gnuplot > wifi.ellipses.png

screenshot

Мы ввели зависимость высоты эллипса от уровня сигнала. Осталось правильно её пересчитать, закрасить полупрозрачностью и опустить вниз на половину. Также gnuplot не обрабатывает точки, находящиеся за предалами xrange, yrange - поэтому к yrange для страховки прибавлено ~0.1

links

social