#!/usr/bin/env python# -*- coding: utf-8 -*-from__future__import(absolute_import,unicode_literals,division,print_function)# from functools import partial, lru_cachefromastropyimportunitsasapuimportnumpyasnpfrom.importcyimtfrom.helperimport_Qinvfrom..importconversionsascnvfrom..importutils# import ipdb__all__=['imt_rural_macro_losses','imt_urban_macro_losses','imt_urban_micro_losses','P_pusch','UE_MIN_MAX_DISTANCES','clutter_imt',]UE_MIN_MAX_DISTANCES={'rural_macro':(10,10000),'urban_macro':(10,5000),'urban_micro':(10,5000),}# UE_MIN_MAX_DISTANCES.__doc__ = '''# Minimum and maximum distances that UEs have from base stations in each of# the IMT propagation models. Below and beyond these, the path losses will# be NaN.# '''# Note, we have to curry the quantities here, because Cython produces# "built-in" functions that don't provide a signature (such that# ranged_quantity_input fails)def_clutter_imt(freq,dist,location_percent,num_end_points=1,):assertnum_end_pointsin[1,2]L_l=23.5+9.6*np.log10(freq)L_s=32.98+23.9*np.log10(dist)+3.0*np.log10(freq)L_clutter=-5*np.log10(np.power(10,-0.2*L_l)+np.power(10,-0.2*L_s))-6*_Qinv(location_percent/100.)ifnum_end_points==2:L_clutter*=2returnL_clutter
[docs]@utils.ranged_quantity_input(freq=(2,67,apu.GHz),dist=(0.25,None,apu.km),location_percent=(0,100,apu.percent),strip_input_units=True,output_unit=cnv.dB)defclutter_imt(freq,dist,location_percent,num_end_points=1,):''' Calculate the Clutter loss according to IMT.CLUTTER document (method 2). Parameters ---------- freq : `~astropy.units.Quantity` Frequency of radiation [GHz] dist : `~astropy.units.Quantity` Distance between Tx/Rx antennas [km] Minimal distance must be 0.25 km (single endpoint clutter) or 1 km (if both endpoints are to be corrected for clutter) location_percent : `~astropy.units.Quantity` Percentage of locations for which the clutter loss `L_clutter` (calculated with this function) will not be exceeded [%] num_end_points : int, optional number of endpoints affected by clutter, allowed values: 1, 2 Returns ------- L_clutter : `~astropy.units.Quantity` Clutter loss [dB] Notes ----- - The algorithm is independent of effective antenna height (w.r.t. clutter height), i.e., it doesn't distinguish between terminals which are close to the ground and those closer to the top of the building. However, the model is only appropriate if the terminal is "in the clutter", below the rooftops. - The result of this function is to be understood as a cumulative value. For example, if `location_percent = 2%`, it means that for 2% of all possible locations, the clutter loss will not exceed the returned `L_clutter` value, for the remaining 98% of locations it will therefore be lower than `L_clutter`. The smaller `location_percent`, the smaller the returned `L_clutter`, i.e., low clutter attenuations are more unlikely. - This model was proposed by ITU study group SG 3 to replace `~pycraf.pathprof.clutter_correction` for IMT 5G studies (especially, at higher frequencies, where multipath effects play a role in urban and suburban areas). '''return_clutter_imt(freq,dist,location_percent,num_end_points=num_end_points,)
[docs]@utils.ranged_quantity_input(freq=(0.5,30,apu.GHz),dist_2d=(0,100000,apu.m),h_bs=(10,150,apu.m),h_ue=(1,10,apu.m),W=(5,50,apu.m),h=(5,50,apu.m),strip_input_units=True,allow_none=False,output_unit=(cnv.dB,cnv.dB,cnv.dimless))defimt_rural_macro_losses(freq,dist_2d,h_bs=35*apu.m,h_ue=1.5*apu.m,W=20*apu.m,h=5*apu.m,):''' Calculate los/non-los propagation losses Rural-Macro IMT scenario. The computation is in accordance with `3GPP TR 38.901 Table 7.4.1-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Parameters ---------- freq : `~astropy.units.Quantity` Frequency of radiation [GHz] dist_2d : `~astropy.units.Quantity` Distance on the ground between BS and UE device [m] Note: Well-defined only for distances between 10 m and 10 km. h_bs : `~astropy.units.Quantity`, optional Basestation height [m] (Default: 35 m) h_ue : `~astropy.units.Quantity`, optional User equipment height [m] (Default: 1.5 m) W : `~astropy.units.Quantity`, optional Average street width [m] (Default: 20 m) h : `~astropy.units.Quantity`, optional Average building height [m] (Default: 5 m) Returns ------- PL_los : `~astropy.units.Quantity` Path loss for line-of-sight cases [dB] PL_nlos : `~astropy.units.Quantity` Path loss for non-line-of-sight cases [dB] los_prob : `~astropy.units.Quantity` Probability for a path to be line-of-sight. [dimless] (see Notes and Examples) Notes ----- - In statistical simulations, the LoS and Non-LoS cases occur with certain probabilities. For sampling of path losses the return parameter `los_prob` can be used, which accounts for the likelihoods according to `3GPP TR 38.901 Table 7.4.2-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Examples -------- .. testsetup:: >>> import numpy as np >>> np.set_printoptions(3) A typical usage, which also accounts for the line-of-sight probabilities, would be:: >>> import numpy as np >>> from pycraf import conversions as cnv >>> from pycraf import pathprof >>> from astropy import units as u >>> from astropy.utils.misc import NumpyRNGContext >>> freq = 1 * u.GHz >>> h_bs, h_ue = 35 * u.m, 1.5 * u.m >>> distances = [5, 20, 1000, 20000] * u.m # 2D distances >>> # Note: too small or large distances will lead to NaN values >>> PL_los, PL_nlos, los_prob = pathprof.imt_rural_macro_losses( ... freq, distances, h_bs=h_bs, h_ue=h_ue ... ) >>> PL_los # doctest: +FLOAT_CMP <Decibel [ nan, 64.381, 94.578, nan] dB> >>> PL_nlos # doctest: +FLOAT_CMP <Decibel [ nan, 65.108, 119.543, nan] dB> >>> los_prob # doctest: +FLOAT_CMP <Quantity [1.000e+00, 9.900e-01, 3.716e-01, 2.082e-09]> >>> # randomly assign LOS or Non-LOS type to UE (according to above prob.) >>> with NumpyRNGContext(0): ... los_type = np.random.uniform(0, 1, distances.size) < los_prob >>> # note: los_type == True : LOS >>> # los_type == False: Non-LOS >>> los_type array([ True, True, False, False]) >>> PL = np.where( ... los_type, PL_los.to_value(cnv.dB), PL_nlos.to_value(cnv.dB) ... ) * cnv.dB >>> PL # doctest: +FLOAT_CMP <Decibel [ nan, 64.381, 119.543, nan] dB> .. testcleanup:: >>> np.set_printoptions() '''pl_los,pl_nlos=cyimt.rural_macro_losses_cython(freq,dist_2d,h_bs,h_ue,W,h,)# probability to have a LOS path;# see 3GPP TR 38.901, Table 7.4.2los_prob=np.exp(-(dist_2d-10)/1000)los_prob[dist_2d<10]=1.los_prob=np.broadcast_to(los_prob,pl_los.shape)returnpl_los,pl_nlos,los_prob
[docs]@utils.ranged_quantity_input(freq=(0.5,30,apu.GHz),dist_2d=(0,100000,apu.m),h_bs=(10,150,apu.m),# according to 3GPP TR 38.901 only 25 m allowed!?h_ue=(1,13,apu.m),# for larger h_ue, need to implement "C" functionh_e=(0,20,apu.m),strip_input_units=True,allow_none=False,output_unit=(cnv.dB,cnv.dB,cnv.dimless))defimt_urban_macro_losses(freq,dist_2d,h_bs=25*apu.m,h_ue=1.5*apu.m,# h_e=1 * apu.m,):''' Calculate los/non-los propagation losses Rural-Macro IMT scenario. The computation is in accordance with `3GPP TR 38.901 Table 7.4.1-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Parameters ---------- freq : `~astropy.units.Quantity` Frequency of radiation [GHz] dist_2d : `~astropy.units.Quantity` Distance on the ground between BS and UE device [m] Note: Well-defined only for distances between 10 m and 5 km. h_bs : `~astropy.units.Quantity`, optional Basestation height [m] (Default: 35 m) h_ue : `~astropy.units.Quantity`, optional User equipment height [m] (Default: 1.5 m) Note: in the `pycraf` implementation this is restricted to `h_ue < 13 m`. 3GPP TR 38.901 also has a model for heights up to 22.5 m, but this involves a `h_e` different from 1 m and is probabilistic, which would make the interface much more complicated. Returns ------- PL_los : `~astropy.units.Quantity` Path loss for line-of-sight cases [dB] PL_nlos : `~astropy.units.Quantity` Path loss for non-line-of-sight cases [dB] los_prob : `~astropy.units.Quantity` Probability for a path to be line-of-sight. [dimless] (see Notes and Examples) Notes ----- - In statistical simulations, the LoS and Non-LoS cases occur with certain probabilities. For sampling of path losses the return parameter `los_prob` can be used, which accounts for the likelihoods according to `3GPP TR 38.901 Table 7.4.2-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Examples -------- .. testsetup:: >>> import numpy as np >>> np.set_printoptions(3) A typical usage, which also accounts for the line-of-sight probabilities, would be:: >>> import numpy as np >>> from pycraf import conversions as cnv >>> from pycraf import pathprof >>> from astropy import units as u >>> from astropy.utils.misc import NumpyRNGContext >>> freq = 1 * u.GHz >>> h_bs, h_ue = 25 * u.m, 1.5 * u.m >>> distances = [5, 20, 1000, 20000] * u.m # 2D distances >>> # Note: too small or large distances will lead to NaN values >>> PL_los, PL_nlos, los_prob = pathprof.imt_urban_macro_losses( ... freq, distances, h_bs=h_bs, h_ue=h_ue ... ) >>> PL_los # doctest: +FLOAT_CMP <Decibel [ nan, 60.766, 108.247, nan] dB> >>> PL_nlos # doctest: +FLOAT_CMP <Decibel [ nan, 71.745, 130.785, nan] dB> >>> los_prob # doctest: +FLOAT_CMP <Quantity [1.000e+00, 9.728e-01, 1.800e-02, 9.000e-04]> >>> # randomly assign LOS or Non-LOS type to UE (according to above prob.) >>> with NumpyRNGContext(0): ... los_type = np.random.uniform(0, 1, distances.size) < los_prob >>> # note: los_type == True : LOS >>> # los_type == False: Non-LOS >>> los_type array([ True, True, False, False]) >>> PL = np.where( ... los_type, PL_los.to_value(cnv.dB), PL_nlos.to_value(cnv.dB) ... ) * cnv.dB >>> PL # doctest: +FLOAT_CMP <Decibel [ nan, 60.766, 130.785, nan] dB> .. testcleanup:: >>> np.set_printoptions() '''# for h_ue > 13 m, need to implement "C" function; then "h_e" is needed!# h_e : `~astropy.units.Quantity`, optional# Effective environment height [m] (Default: 1 m)# Important: for `h_ue > 13 m`, this is subject to random sampling;# see `3GPP TR 38.901 Table 7.4.1-1 Note 1# <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_h_e=1.pl_los,pl_nlos=cyimt.urban_macro_losses_cython(freq,dist_2d,h_bs,h_ue,h_e,)# probability to have a LOS path;# see 3GPP TR 38.901, Table 7.4.2_x=18/dist_2dlos_prob=(# for h_e > 13 m, a correction factor would be necessary!!!_x+np.exp(-18/63/_x)*(1-_x))los_prob[dist_2d<18]=1.los_prob=np.broadcast_to(los_prob,pl_los.shape)returnpl_los,pl_nlos,los_prob
[docs]@utils.ranged_quantity_input(freq=(0.5,100,apu.GHz),dist_2d=(0,100000,apu.m),h_bs=(10,150,apu.m),# according to 3GPP TR 38.901 only 10 m allowed!?h_ue=(1,22.5,apu.m),strip_input_units=True,allow_none=False,output_unit=(cnv.dB,cnv.dB,cnv.dimless))defimt_urban_micro_losses(freq,dist_2d,h_bs=10*apu.m,h_ue=1.5*apu.m,):''' Calculate los/non-los propagation losses Rural-Macro IMT scenario. The computation is in accordance with `3GPP TR 38.901 Table 7.4.1-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Parameters ---------- freq : `~astropy.units.Quantity` Frequency of radiation [GHz] dist_2d : `~astropy.units.Quantity` Distance on the ground between BS and UE device [m] Note: Well-defined only for distances between 10 m and 5 km. h_bs : `~astropy.units.Quantity`, optional Basestation height [m] (Default: 35 m) h_ue : `~astropy.units.Quantity`, optional User equipment height [m] (Default: 1.5 m) Returns ------- PL_los : `~astropy.units.Quantity` Path loss for line-of-sight cases [dB] PL_nlos : `~astropy.units.Quantity` Path loss for non-line-of-sight cases [dB] los_prob : `~astropy.units.Quantity` Probability for a path to be line-of-sight. [dimless] (see Notes and Examples) Notes ----- - In statistical simulations, the LoS and Non-LoS cases occur with certain probabilities. For sampling of path losses the return parameter `los_prob` can be used, which accounts for the likelihoods according to `3GPP TR 38.901 Table 7.4.2-1 <https://portal.3gpp.org/desktopmodules/Specifications/SpecificationDetails.aspx?specificationId=3173>`_ Examples -------- .. testsetup:: >>> import numpy as np >>> np.set_printoptions(3) A typical usage, which also accounts for the line-of-sight probabilities, would be:: >>> import numpy as np >>> from pycraf import conversions as cnv >>> from pycraf import pathprof >>> from astropy import units as u >>> from astropy.utils.misc import NumpyRNGContext >>> freq = 1 * u.GHz >>> h_bs, h_ue = 10 * u.m, 1.5 * u.m >>> distances = [5, 20, 1000, 20000] * u.m # 2D distances >>> # Note: too small or large distances will lead to NaN values >>> PL_los, PL_nlos, los_prob = pathprof.imt_urban_micro_losses( ... freq, distances, h_bs=h_bs, h_ue=h_ue ... ) >>> PL_los # doctest: +FLOAT_CMP <Decibel [ nan, 60.479, 118.534, nan] dB> >>> PL_nlos # doctest: +FLOAT_CMP <Decibel [ nan, 69.599, 128.301, nan] dB> >>> los_prob # doctest: +FLOAT_CMP <Quantity [1.000e+00, 9.574e-01, 1.800e-02, 9.000e-04]> >>> # randomly assign LOS or Non-LOS type to UE (according to above prob.) >>> with NumpyRNGContext(0): ... los_type = np.random.uniform(0, 1, distances.size) < los_prob >>> # note: los_type == True : LOS >>> # los_type == False: Non-LOS >>> los_type array([ True, True, False, False]) >>> PL = np.where( ... los_type, PL_los.to_value(cnv.dB), PL_nlos.to_value(cnv.dB) ... ) * cnv.dB >>> PL # doctest: +FLOAT_CMP <Decibel [ nan, 60.479, 128.301, nan] dB> .. testcleanup:: >>> np.set_printoptions() '''pl_los,pl_nlos=cyimt.urban_micro_losses_cython(freq,dist_2d,h_bs,h_ue,)# probability to have a LOS path;# see 3GPP TR 38.901, Table 7.4.2_x=18/dist_2dlos_prob=(_x+np.exp(-0.5/_x)*(1-_x))los_prob[dist_2d<18]=1.los_prob=np.broadcast_to(los_prob,pl_los.shape)returnpl_los,pl_nlos,los_prob
[docs]@utils.ranged_quantity_input(P_cmax=(-20,50,cnv.dBm),# range pretty large, but what is realistic?P_0_pusch=(-200,100,cnv.dBm),# ditoalpha=(0,1,cnv.dimless),PL=(-100,300,cnv.dB),strip_input_units=True,allow_none=False,output_unit=(cnv.dBm))defP_pusch(P_cmax,M_pusch,P_0_pusch,alpha,PL):''' Calculate power output level after UE power control. See `ITU-R Rec. M.2101-0 <https://www.itu.int/rec/R-REC-M.2101/en>`_ Section 4.1. Parameters ---------- P_cmax : `~astropy.units.Quantity` Maximum transmit power [dBm] M_pusch : `numpy.ndarray`, int Number of allocated resource blocks (RBs) Is this the bandwidth per carrier divided by the RB bandwidth ( typically 180 kHz) and number of UE devices associated to each carrier? P_0_pusch : `numpy.ndarray` Initial receive target UE power level per RB [dBm] alpha : `~astropy.units.Quantity` Balancing factor for UEs with bad channel and UEs with good channel PL : `~astropy.units.Quantity` Path loss between UE and its associated BS [dB] One should use one of the functions `~pathprof.imt_rural_macro_losses`, `~pathprof.imt_urban_macro_losses`, or `~pathprof.imt_urban_micro_losses` to determine PL for the required scenario. Note: Antenna gains should be included, so this is rather the coupling loss than the path loss, unlike what is stated in `ITU-R Rec. M.2101-0 <https://www.itu.int/rec/R-REC-M.2101/en>`_. Returns ------- P_pusch : `~astropy.units.Quantity` Transmit power of the terminal [dBm] Examples -------- .. testsetup:: >>> import numpy as np >>> np.set_printoptions(3) A typical usage would be:: >>> import numpy as np >>> from pycraf import conversions as cnv >>> from pycraf import pathprof >>> from astropy import units as u >>> from astropy.utils.misc import NumpyRNGContext >>> freq = 6.65 * u.GHz >>> h_bs, h_ue = 10 * u.m, 1.5 * u.m >>> distances = [20, 100, 500, 1000] * u.m # 2D distances >>> PL_los, PL_nlos, los_prob = pathprof.imt_urban_micro_losses( ... freq, distances, h_bs=h_bs, h_ue=h_ue ... ) >>> # randomly assign LOS or Non-LOS type to UE (according to above prob.) >>> with NumpyRNGContext(0): ... los_type = np.random.uniform(0, 1, distances.size) < los_prob >>> # note: los_type == True : LOS >>> # los_type == False: Non-LOS >>> PL = np.where( ... los_type, PL_los.to_value(cnv.dB), PL_nlos.to_value(cnv.dB) ... ) * cnv.dB >>> PL # doctest: +FLOAT_CMP <Decibel [ 76.935, 110.581, 135.202, 145.827] dB> >>> # Assume some antenna gains: >>> G_bs, G_ue = 20 * cnv.dBi, 5 * cnv.dBi >>> CL = ( ... PL.to_value(cnv.dB) - ... G_bs.to_value(cnv.dBi) - ... G_ue.to_value(cnv.dBi) ... ) * cnv.dB >>> bw_carrier = 100 * u.MHz >>> bw_rb = 180 * u.kHz # resource block bandwidth >>> num_ue = 3 # 3 UEs per BS sector and carrier >>> M_pusch = int(bw_carrier / bw_rb / num_ue) >>> M_pusch 185 >>> P_cmax = 23 * cnv.dBm >>> P_0_pusch = -95.5 * cnv.dBm >>> alpha = 0.8 * cnv.dimless >>> P_pusch = pathprof.P_pusch( ... P_cmax, M_pusch, P_0_pusch, alpha, CL ... ) >>> P_pusch.to(cnv.dBm) # doctest: +FLOAT_CMP <Decibel [-31.28 , -4.363, 15.333, 23. ] dB(mW)> .. testcleanup:: >>> np.set_printoptions() '''P_pusch=10*np.log10(M_pusch)+P_0_pusch+alpha*PLP_pusch[P_pusch>P_cmax]=P_cmaxreturnP_pusch
if__name__=='__main__':print('This not a standalone python program! Use as module.')