6. LoRaWan – Linealiza por segmentos

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