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:
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 subintervalosse 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()