Para los diferentes entornos donde se propaga la señal, se diferencian por segmentos (fronteras) delimitados en metros desde el gateway.
El desnivel del terreno que existe aproximandamente a 90 mts desde el gateway tiene una sección de "sombra" en donde no se usarán los valores para generar el modelo. por lo que el segundo segmento no se usará para el cálculo, se indica en la variable como [1,0,1]
frontera = [0,90,125,250] # metros
frontera_usar = [1,0,1] # usar
Usando estos parámetros y reordenando el algoritmo anterior para ser usado en cada segmento, se obtiene el siguiente resultado

Para la sección de sombra, para mantener la continuidad en la gráfica, se une el punto final del segmento anterior, y el primer punto del segmento posterior.
Los detalles de cada ecuación por segmentos son:
---- segmento: 0
ecuacion_up ecuacion_down
alpha 2.579875 2.366443
beta -25.528314 -27.974302
eq_latex $ rssi = -10(2.58)log_{10}(d)-25.53 $ $ rssi = -10(2.37)log_{10}(d)-27.97 $
intervalox [1.0, 86.05] [1.0, 86.05]
error_medio 6.17513 6.495915
error_std 7.697343 7.230741
eqg_latex $ d = 10^{(-25.53 - rssi)/(10(2.58))} $ $ d = 10^{(-27.97 - rssi)/(10(2.37))} $
intervaloy [-90.48339483394834, -25.745454545454542] [-82.15682656826569, -26.98787878787879]
errorx_medio 43.575249 35.657177
errorx_std 89.139552 50.786448
---- segmento: 1
ecuacion_up ecuacion_down
alpha 7.068273 6.777292
beta 61.310998 57.364631
eq_latex $ rssi = -10(7.07)log_{10}(d)+61.31 $ $ rssi = -10(6.78)log_{10}(d)+57.36 $
intervalox [86.05, 124.08] [86.05, 124.08]
error_medio 0.0 0.0
error_std 0.0 0.0
eqg_latex $ d = 10^{(61.31 - rssi)/(10(7.07))} $ $ d = 10^{(57.36 - rssi)/(10(6.78))} $
intervaloy [-86.67755403724453, -75.44247020329829] [-84.53164627778156, -73.75907940898257]
errorx_medio 0.0 0.0
errorx_std 0.0 0.0
---- segmento: 2
ecuacion_up ecuacion_down
alpha 4.082007 4.159709
beta -1.212499 2.560248
eq_latex $ rssi = -10(4.08)log_{10}(d)-1.21 $ $ rssi = -10(4.16)log_{10}(d)+2.56 $
intervalox [124.08, 238.66] [126.91, 235.77]
error_medio 3.623762 3.887403
error_std 4.452527 4.535259
eqg_latex $ d = 10^{(-1.21 - rssi)/(10(4.08))} $ $ d = 10^{(2.56 - rssi)/(10(4.16))} $
intervaloy [-105.64163822525596, -81.32270916334662] [-99.64505119453923, -80.30278884462152]
errorx_medio 39.417734 39.102089
errorx_std 52.33983 45.251062
Instrucciones en Python
# ecuación por mínimos cuadrados de una ruta por segmentos
# usando las medidas y distancia al gateway de cada punto
# Revision 2022/07/23
# http://blog.espol.edu.ec/girni/
import numpy as np
import pandas as pd
import os
import json
import matplotlib.pyplot as plt
import girni_lorawan_lib as girni
# INGRESO
carpeta_rsm = "resultado_c_Maiz_todo"
frontera = [0,90,125,250] # metros
segmento_usar = [1,0,1] # usar
arch_var_general = 'variables_generales.txt'
# PROCEDIMIENTO
# carga variables desde archivos
with open(arch_var_general) as linea:
texto = linea.read()
var_gen = json.loads(texto)
globals().update(var_gen)
partes = carpeta_rsm.strip('/').split('_')
arch_nombre = partes[1]+'_'+partes[2]
def linealiza_preparadatos(carpeta_rsm,arch_coord,var_gen):
''' desarrolla la ecuación para un gradiente
'''
var_gen['movAvg_cual'] = [2,4] #cada cuantas muestras
partes = carpeta_rsm.strip('/').split('_')
arch_nombre = partes[1]+'_'+partes[2]
carp_coord = var_gen['carp_coord']
medida = var_gen['medida']
precision = var_gen['precision']
# lee coordenadas y su distancia al gateway
dist_Gtw = girni.coordenadas_leer(arch_coord,carp_coord)
# lista.txt de archivos a usar
arch_lista = medida+"_"+arch_nombre+"_lista.txt"
archivo_ruta = carpeta_rsm + '/' + arch_lista
archivoexiste = os.path.exists(archivo_ruta)
if not(archivoexiste):
puntoUsar_Colum = ['punto', 'up', 'down']
puntoUsar_Tipos = {'punto' : 'object',
'up' : 'int64',
'down' : 'int64'}
puntoUsar = pd.DataFrame(columns = puntoUsar_Colum)
puntoUsar = puntoUsar.astype(dtype = puntoUsar_Tipos)
for unarchivo in os.listdir(carpeta_rsm):
verifica = unarchivo.startswith('describe_')
verifica = verifica and unarchivo.endswith('.csv')
if verifica:
partes = unarchivo.strip('.csv').split('_')
unpunto = partes[2]
puntodato = {'punto' : unpunto,
'up' : 1,
'down' : 1}
puntodato = pd.DataFrame([puntodato])
puntoUsar = pd.concat([puntoUsar,
puntodato],
ignore_index = True)
puntoUsar = puntoUsar.set_index('punto')
puntoUsar.to_csv(archivo_ruta)
# usa lista.txt de archivos seleccionados 1 ó 0 para enlace up,down
if (archivoexiste):
puntoUsar = pd.read_csv(archivo_ruta)
puntoUsar = puntoUsar.set_index('punto')
# Datos para gráfica desde lista.txt
punto_columnas = ['codigopunto','dist_Gtw',
medida+'_up',medida+'_up'+'_std',
'usar_up',medida+'_down',
medida+'_down'+'_std',
'usar_down','dispositivo']
punto_tipos = {'codigopunto': 'object',
'dist_Gtw' : 'float64',
medida+'_up' : 'float64',
medida+'_up'+'_std': 'float64',
'usar_up' : 'int64',
medida+'_down': 'float64',
medida+'_down'+'_std' :'float64',
'usar_down' : 'int64',
'dispositivo': 'object'}
punto_graf = pd.DataFrame(columns=punto_columnas)
punto_graf = punto_graf.astype(dtype=punto_tipos)
puntoSinDist = []
for unarchivo in os.listdir(carpeta_rsm):
if unarchivo.startswith('describe_'):
codigopunto = unarchivo.strip('.csv').split('_')[2]
# lectura del archivo
unresumen = carpeta_rsm+'/'+unarchivo
descriptor = pd.read_csv(unresumen,index_col='Unnamed: 0')
if (codigopunto in dist_Gtw.index):
undato = {'codigopunto': codigopunto,
'dist_Gtw' : dist_Gtw[codigopunto],
medida+'_up' : descriptor['rssi_up']['mean'],
medida+'_up'+'_std': descriptor['rssi_up']['std'],
'usar_up' : puntoUsar['up'][codigopunto],
medida+'_down': descriptor['rssi_down']['mean'],
medida+'_down'+'_std' :descriptor['rssi_down']['std'],
'usar_down' : puntoUsar['down'][codigopunto],
'dispositivo': descriptor['dispositivo'][0]
}
undato = pd.DataFrame([undato])
punto_graf = pd.concat([punto_graf,undato],
ignore_index=True)
else:
puntoSinDist.append(codigopunto)
punto_graf = punto_graf.set_index('codigopunto')
punto_graf = punto_graf.sort_values('dist_Gtw' )
return(punto_graf)
punto_graf = linealiza_preparadatos(carpeta_rsm,arch_coord,var_gen)
# Segmentos, intervalos de indices en tabla ----------
extiende = 15 # metros
intervalos = []
intervalos_ext = []
n_sector = len(frontera)
indices = np.arange(0,len(punto_graf['dist_Gtw']),1)
for i in range(0,n_sector-1,1):
desde = frontera[i]
hasta = frontera[i+1]
# intervalos, indices [a,b]
sector = (punto_graf['dist_Gtw']>=desde)
sector = sector & (punto_graf['dist_Gtw']<hasta)
unintervalo = indices[sector]
a = np.min(unintervalo)
if i>0:
a = a - 1
sector[a] = True
b = np.max(unintervalo)
intervalos.append([a,b])
punto_graf['sector_'+str(i)] = sector
# intervalos extendidos
if (desde-extiende)>0:
desde = desde - extiende
if (hasta + extiende) < np.max(punto_graf['dist_Gtw']):
hasta = hasta + extiende
sector_ext = (punto_graf['dist_Gtw']>=desde)
sector_ext = sector_ext & (punto_graf['dist_Gtw']<=hasta)
unintervalo = indices[sector_ext]
a = np.min(unintervalo)
b = np.max(unintervalo)
intervalos_ext.append([a,b])
def linealiza_segmento(punto_graf,var_gen,segmento=-1):
'''estima la ecuacion para cada segmento,
usada solo cuando segmento >=0 [0,1,2,... ]
'''
precision = var_gen['precision']
resultado = {}
xi = np.array(punto_graf['dist_Gtw'])
# enlace up/down
enlaces =['up','down']
for unenlace in enlaces:
usar = punto_graf['usar_'+unenlace]
if segmento>=0:
sector = punto_graf['sector_'+str(segmento)]
usar = usar*sector
yi = np.array(punto_graf[medida+'_'+unenlace])
# ecuacion
eq = girni.linealiza_lstsq(xi[usar==1],
yi[usar==1],
digitos = precision)
alpha = eq['alpha']
beta = eq['beta']
fd = lambda d: -10*alpha*(np.log10(d))+beta
# puntos de ecuacion en tabla
punto_graf['fi_'+unenlace] = fd(xi)
resultado['ecuacion_'+unenlace] = eq
resultado['tabla'] = punto_graf
return (resultado)
# segmentos a usar primero y último ----------
primero = segmento_usar.index(1)
invertido = segmento_usar.copy()
invertido.reverse()
ultimo = len(segmento_usar) - invertido.index(1) - 1
# ecuaciones para todos los segmentos
eq_intervalo = {}
tabla = {}
for i in range(0,n_sector-1,1):
resultado = linealiza_segmento(punto_graf,var_gen,i)
ecuacion = resultado.copy()
ecuacion.pop('tabla')
ecuacion = pd.DataFrame(ecuacion)
eq_intervalo[i] = ecuacion
tabla[i] = resultado['tabla'].copy()
# intervalos de sombra corrige anteriores
for i in range(0,n_sector-1,1):
condicion = i>primero and i<ultimo
if not(segmento_usar[i]) and i>0 and condicion:
sector = punto_graf['sector_'+str(i)]
# extremos [a,b]
a = intervalos[i-1][1]
b = intervalos[i][1]
# extremo izquierdo a
xa_up = tabla[i]['dist_Gtw'][a]
ya_up = tabla[i-1]['fi_up'][a]
ya_down = tabla[i-1]['fi_down'][a]
# extremo derecho b
xb_up = tabla[i]['dist_Gtw'][b]
yb_up = tabla[i+1]['fi_up'][b]
yb_down = tabla[i+1]['fi_down'][b]
eq_up = girni.linealiza_lstsq([xa_up,xb_up],
[ya_up,yb_up],
digitos = precision)
eq_down = girni.linealiza_lstsq([xa_up,xb_up],
[ya_down,yb_down],
digitos = precision)
ecuacion = {'ecuacion_up' : eq_up,
'ecuacion_down' : eq_down}
ecuacion = pd.DataFrame(ecuacion)
eq_intervalo[i] = ecuacion
# SALIDA
for i in range(0,n_sector-1,1):
print('\n ---- segmento:',i)
print(eq_intervalo[i])
def graf_gradiente_segmento(arch_nombre,punto_graf,ecuacion,var_gen):
''' grafica de gradiente
'''
medida = var_gen['medida']
precision = var_gen['precision']
escalabase = 10 # 10
escala = 'log'
dist = punto_graf['dist_Gtw']
# enlace up/down
enlaces = ['up','down']
colorenlace = ['blue','orange']
i=0
for unenlace in enlaces:
# enlace_up/down
media = punto_graf[medida+'_'+unenlace]
std = punto_graf[medida+'_'+unenlace+'_std']
media_techo = media + std
media_piso = media - std
yi = np.array(media)
fi = punto_graf['fi'+'_'+unenlace]
usar = punto_graf['usar_'+unenlace]
eq = ecuacion['ecuacion_'+unenlace]
dyi_std = eq['error_std']
for j in range(0,len(dist),1):
uncolor = colorenlace[i]
etiqueta = dist.keys()[j]
if not(usar[j]):
uncolor = 'red'
# medida up/down +/- std
graf[i].scatter(dist[j],media[j],
color=uncolor,marker='o')
graf[i].scatter(dist[j],media_techo[j],
color=uncolor,marker='+')
graf[i].scatter(dist[j],media_piso[j],
color=uncolor,marker='+')
# etiquetas por punto
graf[i].annotate(etiqueta,(dist[j],yi[j]),rotation=45)
# linealizado up/down
etiq_gradnt = eq['eq_latex']
etiq_gradnt = etiq_gradnt+' ; ['+str(int(np.min(dist)))
etiq_gradnt = etiq_gradnt+','+str(int(np.max(dist)))+']'
graf[i].plot(dist,fi,label = etiq_gradnt,color=colorenlace[i])
graf[i].plot(dist,fi+dyi_std,color=colorenlace[i],linestyle='dotted')
graf[i].plot(dist,fi-dyi_std,color=colorenlace[i],linestyle='dotted')
# etiquetado de grafico
graf[i].set_ylabel(medida+'_'+enlaces[i]+' (dBm)',color=colorenlace[i])
graf[i].legend()
graf[i].grid(True,linestyle='dotted',axis='x', which='both')
if i==0:
graf[0].set_title(arch_nombre+' '+medida)
if i==1:
graf[1].set_xlabel('distancia')
i = i + 1
return(fig_gradnt)
# grafica gradientes por segmentos
# para limites en eje y
techo_up = pd.DataFrame()
piso_up = pd.DataFrame()
techo_down = pd.DataFrame()
piso_down = pd.DataFrame()
graf=[0,0]
fig_gradnt,graf = plt.subplots(2,1)
escalabase = 10 # 10
escala = 'log'
if escala == 'log':
graf[0].set_xscale(escala,base=escalabase)
graf[1].set_xscale(escala,base=escalabase)
# grafica cada segmento
for i in range(0,n_sector-1,1):
sector = tabla[i]['sector_'+str(i)]
punto_grupo = tabla[i][sector]
#enlace_up
media_up = punto_grupo[medida+'_up']
std_up = eq_intervalo[i]['ecuacion_up']['error_std']
media_up_techo = media_up + std_up
media_up_piso = media_up - std_up
# enlace_down
media_down = punto_grupo[medida+'_down']
std_down = eq_intervalo[i]['ecuacion_up']['error_std']
media_down_techo = media_down + std_down
media_down_piso = media_down - std_down
techo_up = pd.concat([techo_up,media_up_techo])
piso_up = pd.concat([piso_up,media_up_piso])
techo_down = pd.concat([techo_down,media_down_techo])
piso_down = pd.concat([piso_down,media_down_piso])
if segmento_usar[i]:
ecuacion = eq_intervalo[i]
graf_gradiente_segmento(arch_nombre,punto_grupo,ecuacion,var_gen)
condicion = i>primero and i<ultimo
if not(segmento_usar[i]) and i>0 and condicion:
a = tabla[i]['dist_Gtw'][intervalos[i][0]]
b = tabla[i]['dist_Gtw'][intervalos[i][1]]
graf[0].axvspan(a,b,alpha=0.5,color='gray')
graf[1].axvspan(a,b,alpha=0.5,color='gray')
# linea intermedia
xi = punto_grupo['dist_Gtw']
ecuacion = eq_intervalo[i]
fi_up = punto_grupo['fi_up']
fi_down = punto_grupo['fi_down']
graf[0].scatter(xi,media_up,color='blue',marker='o')
graf[0].scatter(xi,media_up_techo,color='blue',marker='+')
graf[0].scatter(xi,media_up_piso,color='blue',marker='+')
graf[1].scatter(xi,media_down,color='orange',marker='o')
graf[1].scatter(xi,media_down_techo,color='orange',marker='+')
graf[1].scatter(xi,media_down_piso,color='orange',marker='+')
alpha_up = eq_up['alpha']
beta_up = eq_up['beta']
fd_up = lambda d: -10*alpha_up*(np.log10(d))+beta_up
fi_up = fd_up(xi)
alpha_down = eq_down['alpha']
beta_down = eq_down['beta']
fd_down = lambda d: -10*alpha_down*(np.log10(d))+beta_down
fi_down = fd_down(xi)
etiq = eq_up['eq_latex']
etiq = etiq+' ; ['+str(int(a))+','+str(int(b))+']'
graf[0].plot(xi,fi_up, label = etiq,
color='blue', linestyle='dashed')
etiq = eq_down['eq_latex']
etiq = etiq+' ; ['+str(int(a))+','+str(int(b))+']'
graf[1].plot(xi,fi_down, label = etiq,
color='brown',linestyle='dashed')
# ajuste de eje y
y_min = np.min([piso_up.min(),piso_down.min()])
y_max = np.max([techo_up.max(),techo_down.max()])
if y_min<-135:
y_min = -135
#graf[0].set_ylim(y_min,y_max)
#graf[1].set_ylim(y_min,y_max)
plt.tight_layout()
unarchivo = carpeta_rsm+'/ecuacion_segmentos_'+arch_nombre+'.png'
fig_gradnt.savefig(unarchivo)
plt.show()