def test_eff_height_within3km(self): profile = [4, 500, 1, 2, 3, 4, 5] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(50, eff_tx_m) eff_tx_m = ehata.CbsdEffectiveHeights(19, profile) self.assertEqual(20, eff_tx_m)
def test_eff_height_over15km(self): profile = [9, 2000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(45.5, eff_tx_m) profile = [12, 2000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(45.5, eff_tx_m)
def test_eff_height_within15km(self): profile = [9, 1000, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(47, eff_tx_m) eff_tx_m = ehata.CbsdEffectiveHeights(21, profile) self.assertEqual(20, eff_tx_m) eff_tx_m = ehata.CbsdEffectiveHeights(19, profile) self.assertEqual(20, eff_tx_m) profile = [5, 800, 12, 2, 3, 4, 5, 9] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(50+((12-7)*1/12.), eff_tx_m)
def test_eff_height_at3km(self): profile = [6, 500, 1, 2, 3, 4, 5, 6, 7] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(50, eff_tx_m) profile = [6, 505, 1, 2, 3, 4, 5, 6, 7] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(50-(6*30/12.e3), eff_tx_m) profile = [6, 500.000000000001, 1, 2, 3, 4, 5, 6, 7] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) #self.assertAlmostEqual(50, eff_tx_m, 7) self.assertEqual(50, eff_tx_m) profile = [6, 499.99999999999507, 1, 2, 3, 4, 5, 6, 7] eff_tx_m = ehata.CbsdEffectiveHeights(50, profile) self.assertEqual(50, eff_tx_m)
def CalcHybridPropagationLoss(lat_cbsd, lon_cbsd, height_cbsd, lat_rx, lon_rx, height_rx, cbsd_indoor=False, reliability=-1, freq_mhz=3625., region='RURAL', is_height_cbsd_amsl=False, return_internals=False): """Implements the Hybrid ITM/eHata NTIA propagation model. As specified by Winforum, see: R2-SGN-03, R2-SGN-04 through R2-SGN-10 and NTIA TR 15-517 Appendix A. Note that contrary to the ITM model, this function does not provide the possibility to retrieve a CDF of path losses, as it is not required in the intended use of that model (it shall be used currently only for protecting zones using average aggregated interference). Warning: Only 'reliability' values 0.5 (median) and -1 (average) are currently fully supported, as other values will not return the quantile when using internally the eHata model. Workaround is to apply it afterwards when the returned opcode=4, and using the standard deviation obtained with the 'GetEHataStdev()' routine. Inputs: lat_cbsd, lon_cbsd, height_cbsd: Lat/lon (deg) and height AGL (m) of CBSD lat_rx, lon_rx, height_rx: Lat/lon (deg) and height AGL (m) of Rx point cbsd_indoor: CBSD indoor status - Default=False. freq_mhz: Frequency (MHz). Default is mid-point of band. reliability: Reliability. Default is -1 (average value). Options: Value in [0,1]: returns the CDF quantile -1: returns the mean path loss region: Region type among 'URBAN', 'SUBURBAN, 'RURAL' is_height_cbsd_amsl: If True, the CBSD height shall be considered as AMSL (Average mean sea level). Returns: A namedtuple of: db_loss: Path Loss in dB. incidence_angles: A namedtuple of angles (degrees) hor_cbsd: Horizontal departure angle (bearing) from CBSD to Rx ver_cbsd: Vertical departure angle at CBSD hor_rx: Horizontal incidence angle (bearing) from Rx to CBSD ver_rx: Vertical incidence angle at Rx internals: A dictionary of internal data for advanced analysis (only if return_internals=True): hybrid_opcode: Opcode from HybridCode - See GetInfoOnHybridCodes() effective_height_cbsd: Effective CBSD antenna height itm_db_loss: Loss in dB for the ITM model. itm_err_num: ITM error code (see wf_itm module). itm_str_mode: Description (string) of dominant prop mode in ITM. dist_km: Distance between end points (km) prof_d_km ndarray of distances (km) - x values to plot terrain. prof_elev ndarray of terrain heightsheights (m) - y values to plot terrain, Raises: Exception if input parameters invalid or out of range. """ # Case of same points if (lat_cbsd == lat_rx and lon_cbsd == lon_rx): return _PropagResult(db_loss=0, incidence_angles=_IncidenceAngles(0, 0, 0, 0), internals=None) # Sanity checks on input parameters if freq_mhz < 40 or freq_mhz > 10000: raise Exception('Frequency outside range [40MHz - 10GHz].') if region not in ['RURAL', 'URBAN', 'SUBURBAN']: raise Exception('Region %s not allowed' % region) if reliability not in (-1, 0.5): raise Exception('Hybrid model only computes the median or the mean.') if is_height_cbsd_amsl: altitude_cbsd = drive.terrain_driver.GetTerrainElevation( lat_cbsd, lon_cbsd) height_cbsd = height_cbsd - altitude_cbsd # Get the terrain profile, using Vincenty great circle route, and WF # standard (bilinear interp; 1501 pts for all distances over 45 km) its_elev = drive.terrain_driver.TerrainProfile(lat1=lat_cbsd, lon1=lon_cbsd, lat2=lat_rx, lon2=lon_rx, target_res_meter=30., do_interp=True, max_points=1501) # Structural CBSD and mobile height corrections height_cbsd = max(height_cbsd, 20.) height_rx = 1.5 # Calculate the predicted ITM loss # and get the distance and profile for use in further logic db_loss_itm, incidence_angles, internals = wf_itm.CalcItmPropagationLoss( lat_cbsd, lon_cbsd, height_cbsd, lat_rx, lon_rx, height_rx, False, reliability, freq_mhz, its_elev, return_internals=True) internals['itm_db_loss'] = db_loss_itm # Calculate the effective heights of the tx height_cbsd_eff = ehata.CbsdEffectiveHeights(height_cbsd, its_elev) internals['effective_height_cbsd'] = height_cbsd_eff # Use ITM if CBSD effective height greater than 200 m if height_cbsd_eff >= 200: return _BuildOutput(db_loss_itm, incidence_angles, internals, HybridMode.ITM_HIGH_HEIGHT, cbsd_indoor) # Set the environment code number. if region == 'URBAN': region_code = 23 elif region == 'SUBURBAN': region_code = 22 else: # 'RURAL': use ITM if not return_internals: return_internals = None return _BuildOutput(db_loss_itm, incidence_angles, internals, HybridMode.ITM_RURAL, cbsd_indoor) # The eHata offset to apply (only in case the mean is requested) offset_median_to_mean = _GetMedianToMeanOffsetDb(freq_mhz, region == 'URBAN') # Now process the different cases dist_km = internals['dist_km'] if not return_internals: return_internals = None if dist_km <= 0.1: # Use Free Space Loss db_loss = CalcFreeSpaceLoss(dist_km, freq_mhz, height_cbsd, height_rx) return _BuildOutput(db_loss, incidence_angles, internals, HybridMode.FSL, cbsd_indoor) elif dist_km > 0.1 and dist_km < 1: # Use E-Hata Median Basic Prop Loss fsl_100m = CalcFreeSpaceLoss(0.1, freq_mhz, height_cbsd, height_rx) median_basic_loss = ehata.MedianBasicPropLoss(freq_mhz, height_cbsd, height_rx, 1, region_code) alpha = 1. + math.log10(dist_km) db_loss = fsl_100m + alpha * (median_basic_loss - fsl_100m) # TODO: validate the following approach with WinnForum participants: # Weight the offset as well from 0 (100m) to 1.0 (1km). if reliability == -1: db_loss += alpha * offset_median_to_mean return _BuildOutput(db_loss, incidence_angles, internals, HybridMode.EHATA_FSL_INTERP, cbsd_indoor) elif dist_km >= 1 and dist_km <= 80: # Use best of E-Hata / ITM ehata_loss_med = ehata.ExtendedHata(its_elev, freq_mhz, height_cbsd, height_rx, region_code) if reliability == 0.5: ehata_loss = ehata_loss_med itm_loss_med = db_loss_itm else: ehata_loss = ehata_loss_med + offset_median_to_mean itm_loss_med = wf_itm.CalcItmPropagationLoss( lat_cbsd, lon_cbsd, height_cbsd, lat_rx, lon_rx, height_rx, False, 0.5, freq_mhz, its_elev).db_loss if itm_loss_med >= ehata_loss_med: return _BuildOutput(db_loss_itm, incidence_angles, internals, HybridMode.ITM_DOMINANT, cbsd_indoor) else: return _BuildOutput(ehata_loss, incidence_angles, internals, HybridMode.EHATA_DOMINANT, cbsd_indoor) elif dist_km > 80: # Use the ITM with correction from E-Hata @ 80km # Calculate the ITM median and eHata median losses at 80km bearing = incidence_angles.hor_cbsd lat_80km, lon_80km, _ = vincenty.GeodesicPoint(lat_cbsd, lon_cbsd, 80., bearing) its_elev_80km = drive.terrain_driver.TerrainProfile( lat_cbsd, lon_cbsd, lat_80km, lon_80km, target_res_meter=30., do_interp=True, max_points=1501) ehata_loss_80km = ehata.ExtendedHata(its_elev_80km, freq_mhz, height_cbsd, height_rx, region_code) itm_loss_80km = wf_itm.CalcItmPropagationLoss(lat_cbsd, lon_cbsd, height_cbsd, lat_80km, lon_80km, height_rx, False, 0.5, freq_mhz, its_elev_80km).db_loss J = max(ehata_loss_80km - itm_loss_80km, 0) db_loss = db_loss_itm + J return _BuildOutput(db_loss, incidence_angles, internals, HybridMode.ITM_CORRECTED, cbsd_indoor)