# pathloss-func.py # (C) 2016 by Harald Welte # All Rights Reserved # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . from Gnumeric import GnumericError, GnumericErrorVALUE import Gnumeric import string import math def lambda_by_mhz(freq_mhz): return 300/freq_mhz def func_fsl(freq_mhz, dist_m): '@FUNCTION=PATHLOSS_FREESPACE\n'\ '@SYNTAX=pathloss_freespace(freq_mhz, dist_m)\n'\ '@DESCRIPTION=Compute Free Space Path Loss (in dB)\n'\ '@SEEALSO=pathloss_egli,pathloss_hata\n' wavelen = lambda_by_mhz(freq_mhz) return -20 * math.log10(wavelen / (4*3.1416*dist_m)) def func_fsl_inverse(freq_mhz, path_loss): '@FUNCTION=RF_RANGE_FREESPACE\n'\ '@SYNTAX=rf_range_freespace(freq_mhz, path_loss_db)\n'\ '@DESCRIPTION=Compute Signal Range (in m) as per Free Space Loss Model\n'\ '@SEEALSO=pathloss_freespace,rf_range_egli,rf_range_hata\n' wavelen = lambda_by_mhz(freq_mhz) dist_m = (wavelen/10**(path_loss/-20)) / (4*3.1416) return dist_m def hata_ch(envi, freq_mhz, rx_ant_m): logfreq = math.log10(freq_mhz) ch = 0 if envi == 'city_small' or envi == 'city_medium': ch = 0.8 + (1.1 * logfreq - 0.7)*rx_ant_m - (1.56 * logfreq) elif envi == 'city_large': if freq_mhz < 200: ch = 8.29 * math.pow(math.log10(1.54*rx_ant_m), 2) - 1.1 elif freq_mhz > 200: ch = 3.2 * math.pow(math.log10(11.75*rx_ant_m), 2) - 4.97 return ch def hata_cm(envi, freq_mhz): logfreq = math.log10(freq_mhz) att = 0 if freq_mhz <= 1500: if envi == 'open_area' or envi == 'rural': # https://en.wikipedia.org/wiki/Hata_model_for_open_areas att = -4.78 * math.pow(logfreq, 2) + 18.33 * logfreq - 40.94 elif envi == 'suburban': # https://en.wikipedia.org/wiki/Hata_model_for_suburban_areas att = -2 * math.pow(logfreq/28, 2) - 5.4 else: if envi == 'city_large': att = 3 else: att = 0 return att def hata_c0(freq_mhz): # http://morse.colorado.edu/~tlen5510/text/classwebch3.html if freq_mhz <= 1500: c0 = 69.55 else: c0 = 46.3 return c0 def hata_cf(freq_mhz): # http://morse.colorado.edu/~tlen5510/text/classwebch3.html if freq_mhz <= 1500: cf = 26.16 else: cf = 33.9 return cf def func_hata(envi, freq_mhz, dist_m, bts_ant_m, ms_ant_m): '@FUNCTION=PATHLOSS_HATA\n'\ '@SYNTAX=pathloss_hata(environment, freq_mhz, dist_m, bts_ant_m, ms_ant_m)\n'\ '@DESCRIPTION=Compute Path Loss (in dB) as per Hata Model\n'\ '@SEEALSO=pathloss_freespace,pathloss_hata\n' # FIXME: valid for 150MHz - 2GHz, MS 1-10m, BS 30-200m, dist # 1-10km environs = ('open_area', 'rural', 'suburban', 'city_small', 'city_medium', 'city_large') if not envi in environs: raise GnumericError,GnumericErrorVALUE # https://en.wikipedia.org/wiki/Hata_model_for_urban_areas hata = hata_c0(freq_mhz) + hata_cf(freq_mhz) * math.log10(freq_mhz) hata -= 13.82 * math.log10(bts_ant_m) hata -= hata_ch(envi, freq_mhz, ms_ant_m) hata += (44.9 - 6.55 * math.log10(bts_ant_m)) * math.log10(dist_m/1000) # subtract correction for open_area / suburban hata += hata_cm(envi, freq_mhz) return hata def func_hata_inverse(envi, freq_mhz, path_loss, bts_ant_m, ms_ant_m): '@FUNCTION=RF_RANGE_HATA\n'\ '@SYNTAX=rf_range_hata(environment, freq_mhz, path_loss_db, bts_ant_m, ms_ant_m)\n'\ '@DESCRIPTION=Compute Signal Range (in m) as per Hata Model\n'\ '@SEEALSO=pathloss_hata,rf_range_freespace,rF_range_egli\n' l = hata_c0(freq_mhz) + hata_cf(freq_mhz) * math.log10(freq_mhz) l -= 13.82 * math.log10(bts_ant_m) l -= hata_ch(envi, freq_mhz, ms_ant_m) l += hata_cm(envi, freq_mhz) # subtract all non-distance related losses from path loss, # remainder is the only term depending on distance r = path_loss - l mult = (44.9 - 6.55 * math.log10(bts_ant_m)) att_dist = r / mult # now we just need the inverse of log10 dist_km = 10 ** att_dist return dist_km * 1000 def func_egli(freq_mhz, dist_m, tx_ant_m, rx_ant_m): '@FUNCTION=PATHLOSS_EGLI\n'\ '@SYNTAX=pathloss_egli(freq_mhz, dist_m, tx_ant_m, rx_ant_m)\n'\ '@DESCRIPTION=Compute Path Loss (in dB) as per Egli Model\n'\ '@SEEALSO=pathloss_freespace,pathloss_hata\n' # https://en.wikipedia.org/wiki/Egli_model att = -20 * math.log10(rx_ant_m*tx_ant_m / (dist_m*dist_m)) att += 20 * math.log10(freq_mhz/40) return att def func_egli_inverse(freq_mhz, path_loss, bts_ant_m, ms_ant_m): '@FUNCTION=RF_RANGE_EGLI\n'\ '@SYNTAX=rf_range_egli(freq_mhz, path_loss_db, bts_ant_m, ms_ant_m)\n'\ '@DESCRIPTION=Compute Signal Range (in m) as per Egli Model\n'\ '@SEEALSO=pathloss_egli,rf_range_freespace,rf_range_hata\n' l = 20 * math.log10(freq_mhz/40) r = path_loss - l x = ms_ant_m*bts_ant_m dist_m = math.sqrt(x/(10**(r/-20))) return dist_m pathlossfunc_functions = { 'pathloss_freespace': ('ff', 'freq_mhz, dist_m', func_fsl), 'rf_range_freespace': ('ff', 'freq_mhz, path_loss', func_fsl_inverse), 'pathloss_egli': ('ffff', 'freq_mhz, dist_m, tx_ant_m, rx_ant_m', func_egli), 'rf_range_egli': ('ffff', 'freq_mhz, path_loss_db, bts_ant_m, ms_ant_m', func_egli_inverse), 'pathloss_hata': ('sffff', 'environ, freq_mhz, dist_m, bts_ant_m, ms_ant_m', func_hata), 'rf_range_hata': ('sffff', 'environ, freq_mhz, path_loss_db, bts_ant_m, ms_ant_m', func_hata_inverse), }