def test_qerfi(): """ Test the qerfi function which is the inverse of qerf - the solution for x to q = Q(x). The approximation is due to Hastings, Jr. (1995). Both the area prediction and point to point prediction modes are tested. """ #test p2p q = [ 0.0100000000000000, 0.100000000000000, 0.500000000000000, 0.900000000000000, 0.990000000000000 ] actual_answer = qerfi(q) expected_answer = [2.3268, 1.2817, 0.0000, -1.2817, -2.3268] assert actual_answer == expected_answer #test area prediction mode q = [0.5000] actual_answer = qerfi(q) expected_answer = [0] assert actual_answer == expected_answer q = [0.5, 0.9, 0.1] actual_answer = qerfi(q) expected_answer = [0.0, -1.2817, 1.2817] assert actual_answer == expected_answer
def itmlogic_p2p(main_user_defined_parameters, surface_profile_m): """ Run itmlogic in point to point (p2p) prediction mode. Parameters ---------- main_user_defined_parameters : dict User defined parameters. surface_profile_m : list Contains surface profile measurements in meters. Returns ------- output : list of dicts Contains model output results. """ prop = main_user_defined_parameters #DEFINE ENVIRONMENTAL PARAMETERS # Terrain relative permittivity prop['eps'] = 15 # Terrain conductivity (S/m) prop['sgm'] = 0.005 # Climate selection (1=equatorial, # 2=continental subtropical, 3=maritime subtropical, # 4=desert, 5=continental temperate, # 6=maritime temperate overland, # 7=maritime temperate, oversea (5 is the default) prop['klim'] = 5 # Surface refractivity (N-units): also controls effective Earth radius prop['ens0'] = 314 #DEFINE STATISTICAL PARAMETERS # Confidence levels for predictions qc = [50, 90, 10] # Reliability levels for predictions qr = [1, 10, 50, 90, 99] # Number of points describing profile -1 pfl = [] pfl.append(len(surface_profile_m) - 1) pfl.append(0) for profile in surface_profile_m: pfl.append(profile) # Refractivity scaling ens=ens0*exp(-zsys/9460.) # (Average system elev above sea level) zsys = 0 # Note also defaults to a continental temperate climate # Setup some intermediate quantities # Initial values for AVAR control parameter: LVAR=0 for quantile change, # 1 for dist change, 2 for HE change, 3 for WN change, 4 for MDVAR change, # 5 for KLIM change prop['lvar'] = 5 # Inverse Earth radius prop['gma'] = 157E-9 # Conversion factor to db db = 8.685890 #Number of confidence intervals requested nc = len(qc) #Number of reliability intervals requested nr = len(qr) #Length of profile in km dkm = prop['d'] #Profile range step, select option here to define range step from profile #length and # of points xkm = 0 #If DKM set <=0, find DKM by mutiplying the profile step by number of #points (not used here) if dkm <= 0: dkm = xkm * pfl[0] #If XKM is <=0, define range step by taking the profile length/number #of points in profile if xkm <= 0: xkm = dkm // pfl[0] #Range step in meters stored in PFL(2) pfl[1] = dkm * 1000 / pfl[0] #Store profile in prop variable prop['pfl'] = pfl #Zero out error flag prop['kwx'] = 0 #Initialize omega_n quantity prop['wn'] = prop['fmhz'] / 47.7 #Initialize refractive index properties prop['ens'] = prop['ens0'] #Scale this appropriately if zsys set by user if zsys != 0: prop['ens'] = prop['ens'] * math.exp(-zsys / 9460) #Include refraction in the effective Earth curvature parameter prop['gme'] = prop['gma'] * (1 - 0.04665 * math.exp(prop['ens'] / 179.3)) #Set surface impedance Zq parameter zq = complex(prop['eps'], 376.62 * prop['sgm'] / prop['wn']) #Set Z parameter (h pol) prop['zgnd'] = np.sqrt(zq - 1) #Set Z parameter (v pol) if prop['ipol'] != 0: prop['zgnd'] = prop['zgnd'] / zq #Flag to tell qlrpfl to set prop.klim=prop.klimx and set lvar to initialize avar routine prop['klimx'] = 0 #Flag to tell qlrpfl to use prop.mdvar=prop.mdvarx and set lvar to initialize avar routine prop['mdvarx'] = 11 #Convert requested reliability levels into arguments of standard normal distribution zr = qerfi([x / 100 for x in qr]) #Convert requested confidence levels into arguments of standard normal distribution zc = qerfi([x / 100 for x in qc]) #Initialization routine for point-to-point mode that sets additional parameters #of prop structure prop = qlrpfl(prop) ## Here HE = effective antenna heights, DL = horizon distances, ## THE = horizon elevation angles ## MDVAR = mode of variability calculation: 0=single message mode, ## 1=accidental mode, 2=mobile mode, 3 =broadcast mode, +10 =point-to-point, ## +20=interference #Free space loss in db fs = db * np.log(2 * prop['wn'] * prop['dist']) #Used to classify path based on comparison of current distance to computed #line-of-site distance q = prop['dist'] - prop['dlsa'] #Scaling used for this classification q = max(q - 0.5 * pfl[1], 0) - max(-q - 0.5 * pfl[1], 0) #Report dominant propagation type predicted by model according to parameters #obtained from qlrpfl if q < 0: print('Line of sight path') elif q == 0: print('Single horizon path') else: print('Double-horizon path') if prop['dist'] <= prop['dlsa']: print('Diffraction is the dominant mode') elif prop['dist'] > prop['dx']: print('Tropospheric scatter is the dominant mode') print('Estimated quantiles of basic transmission loss (db)') print('Free space value {} db'.format(str(fs))) print('Confidence levels {}, {}, {}'.format(str(qc[0]), str(qc[1]), str(qc[2]))) # Confidence levels for predictions qc = [50, 90, 10] # Reliability levels for predictions qr = [1, 10, 50, 90, 99] output = [] for jr in range(0, (nr)): for jc in range(0, nc): #Compute corrections to free space loss based on requested confidence #and reliability quantities avar1, prop = avar(zr[jr], 0, zc[jc], prop) output.append({ 'distance_km': prop['d'], 'reliability_level_%': qr[jr], 'confidence_level_%': qc[jc], 'propagation_loss_dB': fs + avar1 #Add free space loss and correction }) return output
def test_itmlogic_area(): """ Test the model in area prediction mode. """ prop = {} #Antenna height 1 (m), Antenna height 2 (m) prop['hg'] = [3.3, 1.3] #Frequency (MHz) prop['fmhz'] = 20 #Terrain irregularity parameter dh (m) prop['dh'] = 102 #Surface refractivity (N-units) prop['ens0'] = 301 #Relative permittivity of ground prop['eps'] = 15 #Conductivity (S/m) of ground prop['sgm'] = 0.001 #Climate selection (1=equatorial, 2=continental subtropical, #3=maritime subtropical, 4=desert, 5=continental temperate, #6=maritime temperate overland, 7=maritime temperate, #oversea (5 is the default) prop['klimx'] = 5 #0 = horizontal polarization, 1 = vertical prop['ipol'] = 1 #Mode of variability: Single Message=0, Accidental=1, #Mobile=2, Broadcast=3 prop['mdvarx'] = 3 #Percent of time requested for computation QT = [50] #Percent of locations requested for computation QL = [50] #Confidence levels of computation QC = [50, 90, 10] #Initial distance (km) for loop over range D0 = 10 #Max distance 1 (km) for loop over range D1 = 150 #Increment 1 (km) for loop over range DS1 = 10 #Max distance 2 (km) for coarser loop over range (starts beyond D1) D2 = 500 #Increment 2 (km) for coarser loop over range DS2 = 50 #Siting criterion for antenna 1, 0=random, 1= careful, 2= very careful KST = [2, 2] #Refractivity scaling ens=ens0*exp(-zsys/9460.); #(Average system elev above sea level) zsys = 0 #Rescale requested percentages into their corresponding normal distribution arguments ZT = qerfi([x / 100 for x in QT])[0] ZL = qerfi([x / 100 for x in QL])[0] ZC = qerfi([x / 100 for x in QC]) #The number of confidence intervals requested NC = len(QC) #Don't allow negative distances if (D0 <= 0): D0 = DS1 #Set initial distance to 2 km if D0<=0 if (D0 <= 0): D0 = 2 #If final distance less than initial, only do one distance if (D1 <= D0) or (DS1 <= 0): ND = 1 D1 = D0 #Otherwise compute the number of distance points in the loop #and recompute the final distance using this grid else: DS = DS1 ND = math.floor((D1 - D0) / DS + 1.75) D1 = D0 + (ND - 1) * DS #Repeat these corrections for the "coarse" grid in range that follows the fine grid if (D2 <= D1) or (DS2 <= 0): NDC = 0 #If input parameters are wrong, don't do a coarse grid #Otherwise set up appropriate coarse grid else: NDC = ND ND = math.floor((D2 - D1) / DS2 + 0.75) D2 = D1 + ND * DS2 ND = NDC + ND #Standard Earth curvature parameter prop['gma'] = 157E-9 #Scale factor for converting Np/km to dB/km DB = 8.685890 #Scale factor to convert km to m AKM = 1000 #Initialize error flag to 0 prop['kwx'] = 0 #Initialize the omega_n parameter prop['wn'] = prop['fmhz'] / 47.7 #Initialize the surface refractivity prop['ens'] = prop['ens0'] #Adjust surface refractivity parameter if zsys set by user if (zsys != 0): prop['ens'] = prop['ens'] * math.exp(-zsys / 9460) #Implement refractive effects on Earth curvature prop['gme'] = prop['gma'] * (1 - 0.04665 * math.exp(prop['ens'] / 179.3)) #Initialize lvar parameter (this is used when updating AVAR with new input parameters) prop['lvar'] = 0 #Compute ground effective impedance zq parameter zq = complex(prop['eps'], 376.62 * prop['sgm'] / prop['wn']) #Compute ground effective impedance z parameter (horizontal pol) prop['zgnd'] = math.sqrt(zq.real - 1) #Compute ground effective impedance z parameter (vertical pol) if (prop['ipol'] != 0): prop['zgnd'] = prop['zgnd'] / zq #Qlra initializes all the required parameters for area-prediction mode given #siting type and other params already set in "prop" prop = qlra(KST, prop) #Starting distances for loop over range D = D0 #First range step is that of the fine grid DT = DS FS = [] DD = [] output = [] for JD in range(0, ND): #0-22 #Ensure that AVAR routines adjust only for distance (not quantiles which are set) prop['lvar'] = max(1, prop['lvar']) #Compute baseline propagation loss at current range prop = lrprop(D * AKM, prop) #Compute and store baseline loss FS.append(DB * np.log(2 * prop['wn'] * prop['dist'])) #Store distance at this increment DD.append(D) #Loop over confidence intervals requested by user for JC in range(0, NC): #0-3 #Get confidence interval confidence_level = QC[JC] #Compute adjustment for specified confidence levels avar1, prop = avar(ZT, ZL, ZC[JC], prop) #Store results output.append({ 'distance_km': D, 'confidence_level_%': confidence_level, 'propagation_loss_dB': FS[JD] + avar1 #Add in the adjustment for this level }) #Switch to the coarse grid increment in range when we get there if JD + 1 == NDC: DT = DS2 #Increment range D = D + DT for result in output: if result['distance_km'] == 10 and result['confidence_level_%'] == 50: assert result['propagation_loss_dB'] == 111.69200844812511 if result['distance_km'] == 10 and result['confidence_level_%'] == 90: assert result['propagation_loss_dB'] == 121.59437954264777 if result['distance_km'] == 10 and result['confidence_level_%'] == 10: assert result['propagation_loss_dB'] == 101.78963735360244 if result['distance_km'] == 500 and result['confidence_level_%'] == 50: assert result['propagation_loss_dB'] == 215.27421197676375 if result['distance_km'] == 500 and result['confidence_level_%'] == 90: assert result['propagation_loss_dB'] == 221.71014370503855 if result['distance_km'] == 500 and result['confidence_level_%'] == 10: assert result['propagation_loss_dB'] == 208.83828024848896
def run_itmlogic(surface_profile_m, distance_km): """ Run itmlogic in point to point (p2p) prediction mode. Parameters ---------- surface_profile_m : list Contains surface profile measurements in meters. distance_km : float Distance between the transmitter and receiver. Returns ------- output : list of dicts Contains model output results. """ prop = {} #Frequencies used in the campaign (GHz) frequencies = [2.488] #Polarization selection (0=horizontal, 1=vertical) prop['ipol'] = 1 #Receiver heights (m) rxht = [2.56, 2.2] #Transmitter heights (m) (UAV) txht = list(range(10, (130+1), 1)) #Terrain relative permittivity prop['eps'] = 15 #Terrain conductivity (S/m) prop['sgm'] = 0.005 #Surface refractivity (N-units): also controls effective Earth radius -> unknown prop['ens0'] = 314 # # Climate selection (1=equatorial, # # 2=continental subtropical, 3=maritime subtropical, # # 4=desert, 5=continental temperate, # # 6=maritime temperate overland, # # 7=maritime temperate, oversea (5 is the default) prop['klim'] = 5 #Refractivity scaling; (Average system elev above sea level) zsys = 0 #Confidence levels for predictions qc = [50] #Reliability levels for predictions qr = [50] #Preliminary calcs #Conversion factor to dB DB = 8.685890 NC = len(qc) NR = len(qr) ZR = qerfi(qr) #ZR = qerfi(qr / 100) ZC = qerfi(qc) #ZC = qerfi(qc / 100) # Inverse Earth radius prop['gma'] = 157E-9 prop['ens'] = prop['ens0'] if zsys != 0: prop['ens'] = prop['ens'] * math.exp(-zsys / 9460) prop['gme'] = prop['gma'] * (1 - 0.04665 * math.exp(prop['ens'] / 179.3)) PFL = [] PFL.append(len(distance_km)-1) PFL.append(distance_km[1] - distance_km[0]) PFL = PFL + terrain_height_no_tree #Length of profile (km) prop['d'] = distance_km[-1]/1000 prop['pfl'] = PFL output = [] for frequency in range(0, len(frequencies)): prop['fmhz'] = frequencies[frequency] * 1000 prop['wn'] = prop['fmhz'] / 47.7 zq = complex(prop['eps'], (376.62 * prop['sgm'] / prop['wn'])) prop['zgnd'] = np.sqrt(zq - 1) if prop['ipol'] != 0: prop['zgnd'] = prop['zgnd'] / zq if frequency == 0: #Rx height varies with frequency prop['hg'] = [0, 0] prop['hg'][0] = rxht[0] else: prop['hg'] = [0, 0] prop['hg'][0] = rxht[1] #Tx ht (UAV) for iht in range(0, len(txht)): hg = txht[iht] # Antenna 2 height (m) prop['hg'][1] = hg #Setup some intermediate quantities #Initial values for AVAR control parameter: #LVAR=0 for quantile change, 1 for dist change, #2 for HE change, 3 for WN change, # 4 for MDVAR change, 5 for KLIM change prop['lvar'] = 5 #Zero out error flag prop['kwx'] = 0 prop['klimx'] = 0 prop['mdvarx'] = 11 prop = qlrpfl(prop) #Here HE = effective antenna heights #DL = horizon distances #THE = horizon elevation angles #MDVAR = mode of variability calculation: 0=single message mode, #1=accidental mode, 2=mobile mode, 3=broadcast mode, #+10 =point-to-point, +20=interference # Free space loss in dB FS = DB * np.log(2 * prop['wn'] * prop['dist']) for jr in range(0, NR): xlb = [] for jc in range(0, NC): avar1, prop = avar(ZR[jr], 0, ZC[jc], prop) xlb.append(FS + avar1) output.append((prop['fmhz'], hg, qr[jr], xlb[0])) return output