GIRNI

Grupo de Investigación de Redes de iNformación Inalámbricas

4.3 Rssi vs Distancia - Linealiza por intervalos

En áreas extensas de medición donde existen diferentes ambientes o entornos como vegetación en una parte y edificios en otros, el resultado de básico de un solo intervalo puede mejorarse al utilizar subintervalos para cada entorno.

El cambio de entorno forma una frontera, observando la distancia al gateway (baliza) se la toma el valor como punto de partida para estimar la linealización de cada sub-intervalo. Se determinan las ecuaciones para cada intervalo, para calcular los puntos de intersección de cada linea teniendo como resultado una frontera entre diferentes ambientes calculada a partir de las mediciones realizadas.

baliza: 'gtwFIEC'

Por ejemplo, para 'gtwFIEC' y su entorno mostrado en la imagen, en los puntos cercanos existe un ambiente con principalmente vegetación, que luego en puntos más alejados hay ambientes urbanos con edificios de aulas y administrativos.

Partiendo del 'gtwFIEC' se estima una frontera inicial a 176 metros para usar dos subintervalos y realizar la linealización.

 Los resultados que se obtienen son:

Un intervalo

Rssi(d) = -10(4.908)log_{10}(d)+1.406 52.54 \le d \le 397.15

|error_rssi| promedio: 4.84 ,  std: 5.56

|error_dist| promedio: 41.43 ,  std: 54.09

Dos intervalos

Rssi_0(d) = -10(4.263)log_{10}(d)+(-11.269) 52.54 \le d \le 166.14

|error_rssi| promedio: 2.92 , std: 3.33

|error_dist| promedio: 20.19 ,  std: 24.67

Rssi_1(d) = -10(6.09)log_{10}(d)+(29.295) 166.14 \le d \le 397.15

|error_rssi| promedio: 2.71 , std: 3.17

|error_dist| promedio: 25.03 ,  std: 29.93

baliza: 'gtwFCNM'

Para el caso 'gtwFCNM' el área de interés con vegetación se encuentra a partir de los 230 metros, tomado como valor inicial de frontera.


Un intervalo

Rssi(d) = -10(5.403)log_{10}(d)+(8.423) 27.74 \le d \le 364.71

|error_rssi| promedio: 4.59 , std: 5.48

|error_dist| promedio: 41.33 ,  std: 48.24

Dos intervalos

Rssi_0(d) = -10(4.795)log_{10}(d)+(-3.65) 27.74 \le d \le 238.81

|error_rssi| promedio: 5.96 std: 6.51

|error_dist| promedio: 52.15 ,  std: 64.38

Rssi_1(d) = -10(9.027)log_{10}(d)+(96.989) 238.81 \le d \le 364.71

|error_rssi| promedio: 2.81 , std: 3.43

|error_dist| promedio: 20.26 ,  std: 25.06

baliza: 'gtwRECT'

Un intervalo

Rssi(d) = -10(5.054)log_{10}(d)+(11.823) 138.15 \le d \le 451.42

|error_rssi| promedio: 3.11 , std: 3.97

|error_dist| promedio: 42.93 ,  std: 51.69

Dos intervalos

Rssi_0(d) = -10(4.4443)log_{10}(d)+(-3.906) 138.15 \le d \le 358.99

|error_rssi| promedio: 1.38 , std: 1.77

|error_dist| promedio: 18.99 ,  std: 23.13

Rssi_1(d) = -10(5.905)log_{10}(d)+(33.414) 358.99 \le d \le 451.42

|error_rssi| promedio: 1.35 , std: 1.53

|error_dist| promedio: 21.17 ,  std: 24.68


Parámetros

Los parámetros para el algoritmo se establecen en el diccionario 'analiza'. frontera es el vector donde se indica los puntos de corte del intervalo bajo estudio. Si frontera es vacio [], se asume que se analiza todo el intervalo.

Los parámetros usados en el algoritmo para obtener los resultados presentados corresponden a:

# Analizar por segmentos
analiza = {'gtwRECT':{'analizar'  : 1,
                      'atipico_std' : 1,
                      'frontera'    : [320],
                      'atipInterv_std': [1,1],
                      'p_amplia': 4,
                      'grp' : ['RECT','FIEC'],
                      'tip' : ['punto'],
                      'LOS' : [1]},
           'gtwFIEC':{'analizar'  : 1,
                      'atipico_std' : 1,
                      'frontera'    : [190], 
                      'atipInterv_std': [1,1],
                      'p_amplia': 4,
                      'grp' : ['FIEC','FCNM'],
                      'tip' : ['punto'],
                      'LOS' : [1,0]},
           'gtwFCNM':{'analizar'   : 1,
                      'atipico_std' : 1,
                      'frontera'   : [235.0],
                      'atipInterv_std': [2,2],
                      'p_amplia': 4,
                      'grp' : ['FIEC','FCNM'],
                      'tip' : ['punto'],
                      'LOS' : [1,0]}
           }

En la siguiente sección se detalla el algoritmo usado para generar las ecuaciones en cada segmento, las ecuaciones son usadas para estimar la ubicación relativa a cada baliza.

Rssi vs Distancia. Linealiza por intervalos con Python

El algoritmo para realizar dos o más intervalos considera usar un estimado de frontera a lo largo de las mediciones.

Se implementa añadiendo al bloque de ingreso dos parámetros en el bloque 'analiza':

1. frontera es un vector donde se indican las distancia de los puntos de corte de los subintervalos sin considerar los extremos, corresponden a la frontera estimada en el mapa. El algoritmo actualiza la frontera con la intersección  de la linealización de dos subintervalos consecutivos. Si frontera es un vector vacío [] se asume que se trabaja con todo el intervalo.

2. 'atipInterv_std' es un vector para la discriminación de los valores atipicos aplicada en cada subintervalo.

Como referencia para comparar, el resultado del análisis de todo el intervalo se denomina 'r0'. Los subintervalos se identifican por 'r1','r2', etc, en orden al alejarse de la baliza.

En el resultado como valor complementario de revisión se añade el coeficiente de correlación de los puntos usados en el análisis.

Algoritmo en Python

# LoRa-Multipunto, Rssi vs distancia
# linealización Rssi vs log10(distancia)
# por mínimos cuadrados
# Graficas 2D y 3D
# Girni 2020-10-07 propuesta: edelros@espol.edu.ec

import numpy as np
import pandas as pd
import json
import matplotlib.pyplot as plt
import girni_lora_libreria as girni

# INGRESO
# archivos de entrada
modo = 'rx'
medida = 'rssi'
descriptor = 'mean'
arch_medidaubica = 'rsmP06_'+medida+'Ubica01Intervalo.txt'

# archivos de salida
arch_ecuaciones = 'rsmP07_ecuacion01Intervalos.json'

# Analizar por segmentos
analiza = {'gtwRECT':{'analizar' : 1,
 'atipico_std' : 1,
 'frontera' : [320],
 'atipInterv_std': [1,1],
 'p_amplia': 4,
 'grp' : ['RECT','FIEC'],
 'tip' : ['punto'],
 'LOS' : [1]},
 'gtwFIEC':{'analizar' : 1,
 'atipico_std' : 1,
 'frontera' : [190], 
 'atipInterv_std': [1,1],
 'p_amplia': 4,
 'grp' : ['FIEC','FCNM'],
 'tip' : ['punto'],
 'LOS' : [1,0]},
 'gtwFCNM':{'analizar' : 1,
 'atipico_std' : 1,
 'frontera' : [235.0],
 'atipInterv_std': [2,2],
 'p_amplia': 4,
 'grp' : ['FIEC','FCNM'],
 'tip' : ['punto'],
 'LOS' : [1,0]}
 }

baliza = {'d1':'gtwRECT',
 'd2':'gtwFIEC',
 'd3':'gtwFCNM'}

# Parámetros de grafica
tipograf = '2D' # '2D','3D'
escala = 'log' # 'normal','log'
escalabase = 10 # 10, np.exp()
casicero = 1e-4
precision = 3
intersectar = 1 # 0:Falso, 1: Verdadero

# Referencias de gráfica
grupo = ['FIEC' ,'FCNM' ,'RECT','CIRC']
colores = ['green','orange','grey','magenta']
tipo = ['punto','1m' ,'gtw','dispositivo']
marcas = [ 'o','D' ,'D' ,'*' ]

mostrargrpeti = ['FIEC','FCNM','RECT']
mostrartipeti = ['1m','gtw']

# PROCEDIMIENTO
# Resultados de análisis
ecuacion = {}
eq_graf = {}

# leer datos
tabla = pd.read_csv(arch_medidaubica, index_col='etiqueta')
tabla = pd.DataFrame(tabla)

# analiza datos hacia una baliza
for unabaliza in analiza:

 # Parámetros
 analizar = analiza[unabaliza]['analizar']

 if analizar:
 ecuacion[unabaliza] = {}
 eq_graf[unabaliza] = {}
 
 # pares a usar de baliza
 [pares,par_etiqueta] = girni.pares_usar(tabla,baliza,
 analiza,unabaliza,
 medida = medida ,
 modo = modo)
 # todos los puntos
 # analiza puntos para mínimos cuadrados
 xi = pares[:,0]
 yi = pares[:,1]
 n_xi = len(xi)
 # coeficiente de correlación
 correlacion = np.corrcoef(xi,yi)[0,1]
 
 # minimos cuadrados
 ecuacion0 = girni.linealiza_lstsq(xi,yi)

 # selecciona atipicos
 atipico_std = analiza[unabaliza]['atipico_std']
 alpha = ecuacion0['alpha']
 beta = ecuacion0['beta']
 fdist0 = lambda d: -10*alpha*(np.log10(d))+beta
 yi0 = fdist0(xi)
 dyi0std = ecuacion0['error_std']
 dyi0 = yi - yi0
 atipicos = np.abs(dyi0) >= dyi0std*atipico_std
 xi0_e = xi[atipicos]
 yi0_e = yi[atipicos]
 etiq0_e = par_etiqueta[atipicos]

 unintervalo = 'r0' # todos
 # para exportar hacia archivo o gráfica
 ecuacion[unabaliza] = {unintervalo: ecuacion0}
 ecuacion[unabaliza][unintervalo]['correlacion'] = correlacion
 
 eq_graf[unabaliza] = {unintervalo: {'xi_graf':xi,
 'yi_graf':yi,
 'etiqueta': par_etiqueta,
 'linea' : yi0,
 'atipicos':[xi0_e,yi0_e],
 'atip_etiq': etiq0_e}
 }
 
 # Intervalos radiales en sector
 intervalo = [np.min(xi),np.max(xi)]
 frontera = analiza[unabaliza]['frontera']
 if len(frontera)>0:
 # revisar si frontera esta dentro intervalo
 frontera = np.array(frontera, dtype=float)
 revisar = (frontera>=np.min(xi)) & (frontera<=np.max(xi))
 enintervalo = list(frontera[revisar])
 intervalo.extend(enintervalo)
 intervalo = np.array(intervalo)
 ordenar = np.argsort(intervalo)
 intervalo = intervalo[ordenar]
 n_intervalo = len(intervalo)
 
 # analizar cada subintervalo
 p_inicio = 0
 p_desde = 0
 p_amplia = analiza[unabaliza]['p_amplia']
 atipIntv_std = analiza[unabaliza]['atipInterv_std']
 for i in range(0,n_intervalo-1,1):
 i_eq = 'r'+str(i+1) # indice en texto

 # puntos en subintervalo [a,b]
 a = intervalo[i]
 b = intervalo[i+1]
 subintervalo = (xi >= a) & (xi <= b) 
 xi_sub = xi[subintervalo]
 yi_sub = yi[subintervalo]
 n_sub = len(xi_sub)
 etiq_sub = par_etiqueta[p_inicio:p_inicio + n_sub]

 # amplia sub-intervalo, mejora intersecta rectas
 detras = p_inicio
 retrocede = detras
 if detras > p_amplia:
 retrocede = p_amplia
 delante = n_xi - (p_inicio+n_sub)# -1)
 avanza = delante
 if delante >= p_amplia:
 avanza = p_amplia
 p_desde = p_inicio - retrocede
 p_hasta = p_inicio + (n_sub) + avanza
 p_inicio = p_inicio + (n_sub-1)
 
 # subintervalo, amplia puntos
 xi_a = xi[p_desde:p_hasta]
 yi_a = yi[p_desde:p_hasta]
 etiq_a = par_etiqueta[p_desde:p_hasta]
 # coeficiente de correlación
 correlacion1 = np.corrcoef(xi_a,yi_a)[0,1]

 # analiza subintervalo
 ecuacion1 = girni.linealiza_lstsq(xi_a,yi_a)
 ecuacion[unabaliza][i_eq] = ecuacion1
 ecuacion[unabaliza][i_eq]['correlacion'] = correlacion1 
 # atipicos del subintervalo extendido
 alpha = ecuacion1['alpha']
 beta = ecuacion1['beta']
 fdist1 = lambda d: -10*alpha*(np.log10(d))+beta
 yi1 = fdist1(xi_a)
 dyi1std = ecuacion1['error_std']
 atipico_std = analiza[unabaliza]['atipInterv_std'][i]
 dyi1 = yi_a - yi1
 atipicos = np.zeros(len(xi_a),dtype=bool)
 if np.abs(dyi1std) > casicero:
 atipicos = np.abs(dyi1) >= dyi1std*atipico_std
 xi1_e = xi_a[atipicos]
 yi1_e = yi_a[atipicos]
 etiq1_e = etiq_a[atipicos]

 # para gráfica, atipicos sin extender puntos 
 atipicos_sub = (xi1_e >= a) & (xi1_e<=b)
 xi_sub1_e = xi1_e[atipicos_sub]
 yi_sub1_e = yi1_e[atipicos_sub]
 etiq_sub1e = etiq1_e[atipicos_sub]
 eq_graf[unabaliza][i_eq] = {'atipicos': [xi_sub1_e,yi_sub1_e],
 'atip_etiq':etiq_sub1e}
 

 # subintervalo sin atipicos
 if len(xi1_e)>0:
 atipicoNo = np.abs(dyi1) <= dyi1std*atipico_std
 xi2 = xi_a[atipicoNo]
 yi2 = yi_a[atipicoNo]
 etiq2 = etiq_a[atipicoNo]
 # coeficiente de correlación
 correlacion2 = np.corrcoef(xi2,yi2)[0,1]
 ecuacion2 = girni.linealiza_lstsq(xi2,yi2)

 # actualiza ecuación sin atipicos intervaloy
 intervalox = ecuacion1['intervalox']
 ecuacion2['intervalox'] = intervalox.copy()
 alpha = ecuacion2['alpha']
 beta = ecuacion2['beta']
 fdist = lambda d: -10*alpha*(np.log10(d))+beta
 intervaloy = fdist(intervalox)
 ordenar = np.argsort(intervaloy)
 intervaloy = list(intervaloy[ordenar])
 ecuacion2['intervaloy'] = intervaloy
 ecuacion[unabaliza][i_eq] = ecuacion2
 #ecuacion[unabaliza][i_eq]['correlacion1'] = correlacion1
 ecuacion[unabaliza][i_eq]['correlacion'] = correlacion2
 
 # Revisa frontera entre subintervalos,
 # usa intersección de rectas como nueva frontera
 interv_calc = np.copy(intervalo)
 if len(intervalo) >2 and intersectar==1 : 
 for i in range(0,n_intervalo-2,1):
 ai = 'r'+str(i+1)
 bi = 'r'+str(i+2)
 ma = ecuacion[unabaliza][ai]['alpha']
 ba = ecuacion[unabaliza][ai]['beta']
 mb = ecuacion[unabaliza][bi]['alpha']
 bb = ecuacion[unabaliza][bi]['beta']

 # punto de intersección o cruce
 cruzanx = 10**((bb-ba)/(10*(mb-ma)))
 dfrontera = frontera-cruzanx
 # cruce dentro de intervalo de ecuacion
 if cruzanx > intervalo[-1]:
 cruzanx = intervalo[-1]
 if cruzanx < intervalo[0]:
 cruzanx = intervalo[0]
 interv_calc[i+1] = cruzanx
 
 # para grafica evalua cada subintervalo sin atipicos
 n_interv_calc = len(interv_calc)
 for i in range(0,n_interv_calc-1,1):
 i_eq = 'r'+str(i+1)
 a = interv_calc[i]
 b = interv_calc[i+1]
 subintervalo = (xi >= a) & (xi <= b)
 xi_sub = xi[subintervalo]
 yi_sub = yi[subintervalo]
 xi_graf = np.copy(xi[subintervalo])
 if not(a in xi_sub):
 xi_graf = np.concatenate(([a],xi_graf),axis=0)
 if not(b in xi_sub):
 xi_graf = np.concatenate((xi_graf,[b]),axis=0)
 
 # Evalua subintervalo con la ecuacion sin atipicos
 alpha = ecuacion[unabaliza][i_eq]['alpha']
 beta = ecuacion[unabaliza][i_eq]['beta']
 fdist = lambda d: -10*alpha*(np.log10(d))+beta
 yi1_sub = fdist(xi_sub)
 yi_graf = fdist(xi_graf)
 eq_graf[unabaliza][i_eq]['xi_graf'] = xi_graf
 eq_graf[unabaliza][i_eq]['yi_graf'] = yi_graf
 a = np.round(np.min([xi_graf]),precision)
 b = np.round(np.max([xi_graf]),precision)
 ecuacion[unabaliza][i_eq]['intervalox'] = [a,b]
 ay = np.round(np.min([yi_graf]),precision)
 by = np.round(np.max([yi_graf]),precision)
 ecuacion[unabaliza][i_eq]['intervaloy'] = [ay,by] 
 
# SALIDA
for unabaliza in ecuacion:
 print('baliza: ',unabaliza)
 for unaecuacion in ecuacion[unabaliza]:
 unintervalo = ecuacion[unabaliza][unaecuacion]['intervalox']
 unintervaloy = ecuacion[unabaliza][unaecuacion]['intervaloy']
 error_medio = ecuacion[unabaliza][unaecuacion]['error_medio']
 error_std = ecuacion[unabaliza][unaecuacion]['error_std']
 eq_latex = ecuacion[unabaliza][unaecuacion]['eq_latex']
 errorx_medio = ecuacion[unabaliza][unaecuacion]['errorx_medio']
 errorx_std = ecuacion[unabaliza][unaecuacion]['errorx_std']
 correlacion = ecuacion[unabaliza][unaecuacion]['correlacion']

 print('  intervalo: ',unaecuacion)
 print('    ' + eq_latex)
 print('   ','intervalox: ',np.round(unintervalo,precision))
 print('   ','intervaloy: ',np.round(unintervaloy,precision))
 print('    correlación: ',np.round(correlacion,precision))
 print('    |error_rssi| promedio: ',np.round(error_medio,precision),
 ' , std:',np.round(error_std,precision))
 print('    |error_dist| promedio: ',np.round(errorx_medio,precision),
 ' , std:',np.round(errorx_std,precision))
 print()

# salida hacia archivo
with open(arch_ecuaciones, 'w') as outfile:
 json.dump(ecuacion, outfile) 

# GRAFICA
# Referencias para gráfica
grupo = ['FIEC' ,'FCNM' ,'RECT','CIRC']
colores = ['green','orange','grey','magenta']
tipo = ['punto','1m' ,'gtw','dispositivo']
marcas = [ 'o','D' ,'D' ,'*' ]

mostrargrpeti = ['FIEC','FCNM','RECT']
mostrartipeti = ['1m','gtw']

if tipograf=='2D':
 for unabaliza in ecuacion:
 figura,grafica = plt.subplots()
 if escala == 'log':
 grafica.set_xscale(escala,base=escalabase)

 # todos los puntos
 unintervalo = 'r0'
 xi = eq_graf[unabaliza][unintervalo]['xi_graf']
 yi = eq_graf[unabaliza][unintervalo]['yi_graf']
 etiqueta = eq_graf[unabaliza][unintervalo]['etiqueta']
 grafica.scatter(xi,yi,marker='.')
 m = len(xi)
 for i in range(0,m,1):
 grafica.annotate(etiqueta[i],
 (xi[i],yi[i]))
 
 # linea con todos los puntos
 fdtxt = ecuacion[unabaliza][unintervalo]['eq_latex']
 yi0 = eq_graf[unabaliza][unintervalo]['linea']
 a = np.round(np.min([xi]),2)
 b = np.round(np.max([xi]),2)
 eq_texto = fdtxt+' ; ['+str(a)+','+str(b)+']'
 grafica.plot(xi,yi0,
 label = eq_texto,
 linestyle='dotted')

 # lineas por cada subintervalo
 eq_interv = list(ecuacion[unabaliza].keys())
 eq_interv.pop(0)
 n_intervalo = len(eq_interv)

 for i_eq in eq_interv:
 fdtxt = ecuacion[unabaliza][i_eq]['eq_latex']
 xi_graf = eq_graf[unabaliza][i_eq]['xi_graf']
 yi_graf = eq_graf[unabaliza][i_eq]['yi_graf']
 a = np.round(np.min([xi_graf]),precision)
 b = np.round(np.max([xi_graf]),precision)
 eq_texto = fdtxt+' ; ['+str(a)+','+str(b)+']'
 grafica.plot(xi_graf,yi_graf,
 label = eq_texto)

 # atipicos marcados en subintervalo
 [xi1_e,yi1_e] = eq_graf[unabaliza][i_eq]['atipicos']
 etiq1_e = eq_graf[unabaliza][i_eq]['atip_etiq']
 grafica.scatter(xi1_e,yi1_e, color='red')
 m = len(etiq1_e)
 for i in range(0,m,1):
 grafica.annotate(etiq1_e[i],
 (xi1_e[i],yi1_e[i]),
 color='red')
 
 # lineas de frontera
 grafica.axvline(a, color='lightblue')
 valor_frontera = str(np.round(a,precision))
 grafica.annotate(valor_frontera,
 (a,np.max([yi,yi0])),
 color='lightblue')
 grafica.axvline(b, color='lightblue')
 valor_frontera = str(np.round(b,precision))
 grafica.annotate(valor_frontera,
 (b,np.max([yi,yi0])),
 color='lightblue') 
 
 # etiquetas y títulos
 grafica.legend()
 grafica.set_ylabel(medida+'_'+modo)
 grafica.set_xlabel('distancia')
 grafica.grid(True,linestyle='dotted',
 axis='x', which='both')
 
 untitulo = unabaliza+': '+medida+'_'+modo + ' vs distancia'
 grafica.set_title(untitulo)
 
 plt.show()