Visualizzare la potenza del segnale wireless

Spread the love

Utilizzo il mio smartphone come hotspot per connettermi a Internet con i dispositivi di casa. Stamattina ho voluto controllare quale fosse la potenza del segnale perché volevo trovare la posizione ottimale dell’hotspot rispetto alla mia postazione di lavoro.

Prima mi sono studiato come accedere al dato della potenza del segnale in ingresso dell’antenna WiFi e poi ho riportato questo dato in funzione del tempo in un grafico animato in modo tale da poter girar per casa e posizionarlo dove ho un segnale più forte, e non è detto che sia accanto al PC 😉

Campionamento della potenza del segnale WiFi

La potenza istantanea del segnale si può visualizzare con il comando

$ iwconfig wlp2s0
wlp2s0 IEEE 802.11 ESSID:"HUAWEI Y6 2019"
Mode:Managed Frequency:2.412 GHz Access Point: F2:E4:A2:93:8C:0C
Bit Rate=72.2 Mb/s Tx-Power=22 dBm
Retry short limit:7 RTS thr:off Fragment thr:off
Power Management:on
Link Quality=61/70 Signal level=-49 dBm
Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0
Tx excessive retries:171 Invalid misc:9250 Missed beacon:0

Come si vede, la linea

Link Quality=61/70 Signal level=-49 dBm

contiene l’informazione desiderata. Se spegniamo l’hotspot l’output del comando è invece:

$ iwconfig wlp2s0
wlp2s0 IEEE 802.11 ESSID:off/any
Mode:Managed Access Point: Not-Associated Tx-Power=22 dBm
Retry short limit:7 RTS thr:off Fragment thr:off
Power Management:on

Quindi un modo se volete un po’ grezzo di accedere velocemente alla potenza del segnale è fare il parsing dell’output di questo comando. L’idea è di lanciarlo con regolarità in modo da costruire un campione di dati da visualizzare in qualche modo in tempo reale.

Il primo programma che ho scritto è questo:

#!/usr/bin/python3
#
# Found in: https://www.calazan.com/how-to-continuously-monitor-your-wi-fis-signal-strength-in-ubuntu/
# Mutatis mutandi
#

import subprocess
import time
import argparse

parser = argparse.ArgumentParser(description='Display WLAN signal strength.')
parser.add_argument(dest='interface', nargs='?', default='wlan0',
                    help='wlan interface (default: wlan0)')
args = parser.parse_args()

print('\n---Press CTRL+Z or CTRL+C to exit---\n')

while True:
    cmd = subprocess.Popen('iwconfig %s' % args.interface, shell=True,
                           stdout=subprocess.PIPE)
                           
    for line in cmd.stdout:
        l = str(line)
        if 'Link Quality' in l:
            print (l),
        elif 'Not-Associated' in l:
            print ('No signal')
    time.sleep(1)

Aspetti importanti di questo script sono il package subprocess che consente a Python di lanciare un comando del sistema operativo, in questo caso

iwconfig wlp2s0

Il package argparser fornisce una serie di utilissime funzioni per maneggiare le linee di comando (definire i parametri di avvio, la loro obbligatorietà, i valori di default e finanche la documentazione di help).

Il package itertools fornisce il metodo count() che realizza un contatore progressivo che ci serve per etichettare i campioni di segnale, campioni che vengono prelevati con regolarità utilizzando le funzione del package time..

In sostanza il metodo subprocess.Popen() lancia il comando e il dato desiderato si trova nell’output del comando che comunque non è una stringa e va convertito per potere fare il parsing. Il parsing si utilizza quando il dato è annegato nella sporcizia, per cui ritengo questo mio metodo un metodo piuttosto sporco.

In questo embrione di programma ho il seguente ouput

—Press CTRL+Z or CTRL+C to stop.—

b' Link Quality=70/70 Signal level=-31 dBm \n'
b' Link Quality=70/70 Signal level=-31 dBm \n'
b' Link Quality=70/70 Signal level=-36 dBm \n'
b' Link Quality=70/70 Signal level=-32 dBm \n'
b' Link Quality=70/70 Signal level=-32 dBm \n'

Il dato interessante si trova in ogni riga dalla posizione 45 alla posizione 48.

Con una piccola modifica ci facciamo stampare solo il dato desiderato

--Press CTRL+Z or CTRL+C to stop.---
-32
-32
-35
-31
-31
-35
-35
-35
-36

Ora invece di stampare questi valori, li voglio disporre su un grafico. Utilizzo la libreria matplotlib. Il sorgente completo è il seguente

#!/usr/bin/python3
#
# matplotlib @ https://youtu.be/Ercd-Ip5PfQ
# subprocess @ https://bit.ly/3041a3m
# Adopted for reading wifi power signal and plotting it
#
# @author: marco@betaingegneria.it
# @copyright: GPL Affero v.3
# @date 2020-09-25

import subprocess
import argparse
import matplotlib.pyplot as plt
from itertools import count
from matplotlib.animation import FuncAnimation

# init graphics stuff
plt.style.use('seaborn')

x=[] # time series
y=[] # power samples
index = count()

# init wlan signal reader
# parse line command arguments
parser = argparse.ArgumentParser(description='Display WLAN signal strength.')

# interface
parser.add_argument(dest='interface', nargs='?', default='wlp2s0',
                    help='wireless.py [interface=wlp2s0] [delta=800]')

# sampling period in ms
parser.add_argument(dest='delta', nargs='?', default='800',
                    help='wireless.py [interface=wlp2s0] [delta=800]')
                    
args = parser.parse_args()

if (int(args.delta) < 200):
    print('Please, choose a value >= 200 for delta')
    exit()

print('Close the window to exit')

def animate(i):
    """ read the wifi power in dBm and plot """
    x.append(next(index))
    
    cmd = subprocess.Popen('iwconfig %s' % args.interface, shell=True,
                       stdout=subprocess.PIPE)
                       
    for line in cmd.stdout:
        l = str(line)
        if 'Link Quality' in l:
            y.append(int(l[45:48]))
        elif 'Not-Associated' in l:
            y.append(None)

    plt.cla()
    plt.plot(x, y, '-', marker='.', label='WiFi signal strenght in dBm')
    plt.legend(loc='upper left')
    plt.tight_layout()
    
    # ring a bell every sample
    #print('\a')

ani = FuncAnimation(plt.gcf(), animate, interval=args.delta)    

plt.show()

Un po’ di parafrasi: la funzione animate() contiene il codice che legge la potenza e, eseguendo il parsing estraiamo il valore in dBm da accodare alla lista y, mentre nella lista x accodiamo i valori del contatore.

Il plot viene fatto utilizzando come skin lo stile seaborn, che comprende la griglia in negativo (bianco su grigio).

Infine ho utilizzato il metodo matplotlib.animation() che può essere configurato per un aggiornamento ogni delta millisecondi – ci sganciamo quindi dalla libreria time – , variabile che gestiamo come parametro di input.

Il risultato è questo

Andamento della potenza segnale WiFi provando a spostare l’hotspot in giro per la stanza

Se si spegne l’hotspot o si esce dal suo range di segnale non verrò plottato alcun dato.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.