forked from xuxiaoli-seu/SNARM-UAV-Learning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
radio_environment.py
357 lines (311 loc) · 15 KB
/
radio_environment.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# -*- coding: utf-8 -*-
"""
Created on Wed Nov 13 10:36:06 2019
@author: Xiaoli Xu and Yong Zeng
At each UAV location, get the empirical outage probability based on the measured signal strengths
"""
import numpy as np
import matplotlib.pyplot as plt
import random
#import time
print('Generate radio environment......')
# This part model the distribution of buildings
ALPHA=0.3
BETA=300
GAMA=50
MAXHeight=90
SIR_THRESHOLD=0 #SIR threshold in dB for outage
#==========================================
#==Simulate the building locations and building size. Each building is modeled by a square
D=2 #in km, consider the area of DxD km^2
N=BETA*(D**2) #the total number of buildings
A=ALPHA*(D**2)/N #the expected size of each building
Side=np.sqrt(A)
H_vec=np.random.rayleigh(GAMA,N)
H_vec=[min(x, MAXHeight) for x in H_vec]
#Grid distribution of buildings
Cluster_per_side=3
Cluster=Cluster_per_side**2
N_per_cluster=[np.ceil(N/Cluster) for i in range(Cluster)]
#Add some modification to ensure that the total number of buildings is N
Extra_building=int(np.sum(N_per_cluster)-N)
N_per_cluster[:(Extra_building-1)]=[np.ceil(N/Cluster)-1 for i in range(Extra_building)]
#============================
Road_width=0.02 #road width in km
Cluster_size=(D-(Cluster_per_side-1)*Road_width)/Cluster_per_side
Cluster_center=np.arange(Cluster_per_side)*(Cluster_size+Road_width)+Cluster_size/2
#=====Get the building locations=================
XLOC=[];
YLOC=[];
for i in range(Cluster_per_side):
for j in range(Cluster_per_side):
Idx=i*Cluster_per_side+j
Buildings=int(N_per_cluster[Idx])
Center_loc=[Cluster_center[i],Cluster_center[j]]
Building_per_row=int(np.ceil(np.sqrt(Buildings)))
Building_dist=(Cluster_size-2*Side)/(Building_per_row-1)
X_loc=np.linspace((-Cluster_size+2*Side)/2,(Cluster_size-2*Side)/2,Building_per_row)
Loc_tempX=np.array(list(X_loc)*Building_per_row)+Center_loc[0]
Loc_tempY=np.repeat(list(X_loc),Building_per_row)+Center_loc[1]
XLOC.extend(list(Loc_tempX[0:Buildings]))
YLOC.extend(list(Loc_tempY[0:Buildings]))
#Sample the building Heights
step=101 #include the start point at 0 and end point, the space between two sample points is D/(step-1)
HeightMapMatrix=np.zeros(shape=(D*step,D*step))
HeighMapArray=HeightMapMatrix.reshape(1,(D*step)**2)
for i in range(N):
x1=XLOC[i]-Side/2
x2=XLOC[i]+Side/2
y1=YLOC[i]-Side/2
y2=YLOC[i]+Side/2
HeightMapMatrix[int(np.ceil(x1*step)-1):int(np.floor(x2*step)),int(np.ceil(y1*step)-1):int(np.floor(y2*step))]=H_vec[i]
#=================END of Building distributions================================
#=============Define the BS Distribution=======================
BS_loc=np.array([[1, 1, 0.025], [1.5774,1.333, 0.025], [1, 1.6667,0.025,], [0.4226,1.3333, 0.025], [0.4226, 0.6667, 0.025], [1, 0.3333, 0.025], [1.5774,0.6667, 0.025]])
#BS_Height=25 #BS height in meters convert to location in km
BS_thetaD=100 # The downtile angle in degree [0, 180]
PB=0.1 #BS Transmit power in Watt
Fc=2 # Operating Frequency in GHz
LightSpeed=3*(10**8)
WaveLength=LightSpeed/(Fc*(10**9)) #wavelength in meter
SectorBoreSiteAngle=[-120,0,120] #the sector angle for each BS
Sec_num=np.size(SectorBoreSiteAngle) #the number of sectors per cell
FastFadingSampleSize=1000 #number of signal measurements per time step
#===========View Building and BS distributions
plt.figure()
for i in range(N):
x1=XLOC[i]-Side/2
x2=XLOC[i]+Side/2
y1=YLOC[i]-Side/2
y2=YLOC[i]+Side/2
XList=[x1,x2,x2,x1,x1]
YList=[y1,y1,y2,y2,y1]
plt.plot(XList,YList,'r-')
plt.plot(BS_loc[:,0],BS_loc[:,1],'bp',markersize=5)
#==============================================================================
#To speed up, we pre-store the atenna gain from different angles into a matrix
def getAntennaGain(Theta_deg,Phi_deg):
#Basic Setting about Antenna Arrays
ArrayElement_Horizontal=1 #number of array elements in horizontal
ArrayElement_Vertical=8
DV=0.5*WaveLength #spacing for vertical array
DH=0.5*WaveLength #spacing for horizontal array
angleTiltV=BS_thetaD
angleTiltH=0 #horizontal tilt angle
#Find the element power gain
angle3dB=65
Am=30
AH=-np.min([12*(Phi_deg/angle3dB)**2,Am]) #element power gain in horizontal
AV=-np.min([12*((Theta_deg-90)/angle3dB)**2,Am]) #element power gain in Vertical
Gemax=8 # dBi antenna gain in dB above an isotropic radiator, Maximum directional gain of an antenna element
Aelement=-np.min([-(AH+AV),Am])
GelementdB=Gemax+Aelement #dBi
Gelement=10**(GelementdB/10)
Felement=np.sqrt(Gelement)
#Find array gain
k=2*np.pi/WaveLength #wave number
kVector=k*np.array([np.sin(Theta_deg/180*np.pi)*np.cos(Phi_deg/180*np.pi),np.sin(Theta_deg/180*np.pi)*np.sin(Phi_deg/180*np.pi),np.cos(Theta_deg/180*np.pi)]) #wave vector
rMatrix=np.zeros(shape=(ArrayElement_Horizontal*ArrayElement_Vertical,3))
for n in range(ArrayElement_Horizontal):
rMatrix[(n+1)*np.arange(ArrayElement_Vertical),2]=np.arange(ArrayElement_Vertical)*DV
rMatrix[(n+1)*np.arange(ArrayElement_Vertical),1]=n*DH
SteeringVector=np.exp(-1j*(rMatrix.dot(np.transpose(kVector))))
#Vertical Weight Vector
Weight_Vertical=(1/np.sqrt(ArrayElement_Vertical))*np.exp(-1j*k*np.arange(ArrayElement_Vertical)*DV*np.cos(angleTiltV/180*np.pi))
Weight_Horizontal=(1/np.sqrt(ArrayElement_Horizontal))*np.exp(-1j*k*np.arange(ArrayElement_Horizontal)*DH*np.sin(angleTiltH/180*np.pi))
Weight2D=np.kron(Weight_Horizontal,np.transpose(Weight_Vertical))
WeightFlatten=Weight2D.reshape(1,ArrayElement_Vertical*ArrayElement_Horizontal)
ArrayFactor=np.conjugate(WeightFlatten).dot(SteeringVector.reshape(ArrayElement_Vertical,1))
Farray=Felement*ArrayFactor
Garray=(np.abs(Farray))**2
return 10*np.log10(Garray),Farray
#================get All the Antenna gain====================
angleVvector=np.arange(181) #vertical angle from 0 to 180
angleHvector=np.linspace(-180,179,360)
numV=np.size(angleVvector)
numH=np.size(angleHvector)
GarraydBmatrix=np.zeros(shape=(numV,numH)) #pre-stored atenna gain
FtxMatrix=np.zeros(shape=(numV,numH),dtype=complex) #pre-stored array factor
for p in range(numV):
for q in range(numH):
GarraydBmatrix[p,q],FtxMatrix[p,q]=getAntennaGain(angleVvector[p],angleHvector[q])
#==============================================================================
#=========Main Function that determines the best outage from all BS at a given location=======
#loc_vec: a matrix, nx3, each row is a (x,y,z) location
#SIR_th: the SIR threshold for determining outage
def getPointMiniOutage(loc_vec):
numLoc=len(loc_vec)
Out_vec=[]
for i in range(numLoc):
PointLoc=loc_vec[i,:]
OutageMatrix=getPointOutageMatrix(PointLoc,SIR_THRESHOLD)
MiniOutage=np.min(OutageMatrix)
Out_vec.append(MiniOutage)
return Out_vec
#For a given location, return the empirical outage probaibility from all sectors of all BSs
#PointLoc: the given point location
#SIR_th: the SIR threshold for defining the outage
#OutageMatrix: The average outage probability for connecting with each site, obtained by averaging over all the samples
def getPointOutageMatrix(PointLoc,SIR_th):
numBS=len(BS_loc)
SignalFromBS=[]
TotalPower=0
for i in range(len(BS_loc)):
BS=BS_loc[i,:]
LoS=checkLoS(PointLoc,BS)
MeasuredSignal=getReceivedPower_RicianAndRayleighFastFading(PointLoc,BS,LoS)
SignalFromBS.append(MeasuredSignal)
TotalPower=TotalPower+MeasuredSignal
TotalPowerAllSector=np.sum(TotalPower, axis=1) #the interference of all power
OutageMatrix=np.zeros(shape=(numBS,Sec_num))
for i in range(len(BS_loc)):
SignalFromThisBS=SignalFromBS[i]
for sector in range(Sec_num):
SignalFromThisSector=SignalFromThisBS[:,sector]
SIR=SignalFromThisSector/(TotalPowerAllSector-SignalFromThisSector)
SIR_dB=10*np.log10(SIR)
OutageMatrix[i,sector]=np.sum(SIR_dB<SIR_th)/len(SIR_dB)
return OutageMatrix
#Return the received power at a location from all the three sectors of a BS
#While the large scale path loss power is a constant for given location and site, the fast fading may change very fast.
#Hence, we return multiple fast fading coefficients. The number of samples is determined by FastFadingSampleSize
#A simple fast-fading implementation: if LoS, Rician fading with K factor 15 dB; otherwise, Rayleigh fading
def getReceivedPower_RicianAndRayleighFastFading(PointLoc,BS,LoS):
HorizonDistance=np.sqrt((BS[0]-PointLoc[0])**2+(BS[1]-PointLoc[1])**2)
Theta=np.arctan((BS[2]-PointLoc[2])/HorizonDistance) #elevation angle
Theta_deg=np.rad2deg(Theta)+90 #convert to the (0,180) degree
if (PointLoc[1]==BS[1])&(PointLoc[0]==BS[0]):
Phi=0
else:
Phi=np.arctan((PointLoc[1]-BS[1])/(PointLoc[0]-BS[0]+0.00001)) # to avoid dividing by 0
Phi_deg=np.rad2deg(Phi)
#Convert the horizontal degree to the range (-180,180)
if (PointLoc[1]>BS[1])&(PointLoc[0]<BS[0]):
Phi_deg=Phi_deg+180
elif (PointLoc[1]<BS[1])&(PointLoc[0]<BS[0]):
Phi_deg=Phi_deg-180
LargeScale=getLargeScalePowerFromBS(PointLoc,BS,Theta_deg,Phi_deg,LoS) #large-scale received power based on path loss
#the random component, which is Rayleigh fading
RayleighComponent=np.sqrt(0.5)*(np.random.randn(FastFadingSampleSize,3)+1j*np.random.randn(FastFadingSampleSize,3))
if LoS:#LoS, fast fading is given by Rician fading with K factor 15 dB
K_R_dB=15 #Rician K factor in dB
K_R=10**(K_R_dB/10)
threeD_distance=1000*np.sqrt((BS[0]-PointLoc[0])**2+(BS[1]-PointLoc[1])**2+(BS[2]-PointLoc[2])**2)#3D distance in meter
DetermComponent=np.exp(-1j*2*np.pi*threeD_distance/WaveLength) #deterministic component
AllFastFadingCoef=np.sqrt(K_R/(K_R+1))*DetermComponent+np.sqrt(1/(K_R+1))*RayleighComponent
else:#NLoS, fast fading is Rayleigh fading
AllFastFadingCoef=RayleighComponent
h_overall=AllFastFadingCoef*np.sqrt(np.tile(LargeScale,(FastFadingSampleSize,1)))
PowerInstant=np.abs(h_overall)**2 #the instantneous received power in Watt
return PowerInstant
#This function check whether there is LoS between the BS and the given Loc
def checkLoS(PointLoc,BS):
SamplePoints=np.linspace(0,1,100)
XSample=BS[0]+SamplePoints*(PointLoc[0]-BS[0])
YSample=BS[1]+SamplePoints*(PointLoc[1]-BS[1])
ZSample=BS[2]+SamplePoints*(PointLoc[2]-BS[2])
XRange=np.floor(XSample*(step-1))
YRange=np.floor(YSample*(step-1)) #
XRange=[max(x, 0) for x in XRange] #remove the negative idex
YRange=[max(x, 0) for x in YRange] #remove the negative idex
Idx_vec=np.int_((np.array(XRange)*D*step+np.array(YRange)))
SelectedHeight=[HeighMapArray[0,i] for i in Idx_vec]
if any([x>y for (x,y) in zip(SelectedHeight, ZSample)]):
return False
else:
return True
def getLargeScalePowerFromBS(PointLoc,BS,Theta_deg,Phi_deg,LoS):
Sector_num=len(SectorBoreSiteAngle)
Phi_Sector_ref=Phi_deg-np.array(SectorBoreSiteAngle)
#Convert to the range (-180,180) with respect to the sector angle
Phi_Sector_ref[Phi_Sector_ref<-180]=Phi_Sector_ref[Phi_Sector_ref<-180]+360
Phi_Sector_ref[Phi_Sector_ref>180]=Phi_Sector_ref[Phi_Sector_ref>180]-360
ChGain_dB=np.zeros(shape=(1,Sector_num))
for i in range(Sector_num):
ChGain_dB[0,i],null=getAntennaGain(Theta_deg,Phi_Sector_ref[i])
ChGain=np.power(10,ChGain_dB/10) #choose the sector that provides the maximal channel gain
Distance=1000*np.sqrt((BS[0]-PointLoc[0])**2+(BS[1]-PointLoc[1])**2+(BS[2]-PointLoc[2])**2) #convert to meter
#We use 3GPP TR36.777 Urban Macro Cell model to generate the path loss
#UAV height between 22.5m and 300m
if LoS:
PathLoss_LoS_dB=28+22*np.log10(Distance)+20*np.log10(Fc)
PathLoss_LoS_Linear=10**(-PathLoss_LoS_dB/10)
Prx=ChGain*PB*PathLoss_LoS_Linear
else:
PathLoss_NLoS_dB=-17.5+(46-7*np.log10(PointLoc[2]*1000))*np.log10(Distance)+20*np.log10(40*np.pi*Fc/3)
PathLoss_NLoS_Linear=10**(-PathLoss_NLoS_dB/10)
Prx=ChGain*PB*PathLoss_NLoS_Linear
return Prx
##the antenna gain viewed from vertical plane
#plt.axes(polar=True)
#plt.plot(np.deg2rad(angleVvector),GarraydBmatrix[:,180],c='k')
#plt.title('Vertical Antenna Gain with azimuth angle 0')
#plt.show()
#
##The antenna gain viewed from the horizontal plane
#plt.axes(polar=True)
#plt.plot(np.deg2rad(angleHvector),GarraydBmatrix[101,:],c='k')
#plt.title('Horizontal Antenna Gain with vertial angle 90')
#plt.show()
#
## 3D antenna gain
#THETA, PHI= np.meshgrid(np.deg2rad(angleHvector), np.deg2rad(angleVvector))
#R = GarraydBmatrix
#Rmax = np.max(R)
#
#X = R * np.sin(THETA) * np.cos(PHI)
#Y = R * np.sin(THETA) * np.sin(PHI)
#Z = R * np.cos(THETA)
#
#fig = plt.figure()
#ax = fig.add_subplot(1,1,1, projection='3d')
#plot = ax.plot_surface(
# X, Y, Z, rstride=1, cstride=1, cmap=plt.get_cmap('jet'),
# linewidth=0, antialiased=False, alpha=0.5)
#
#ax.view_init(30, 0)
#plt.show()
#======================================================
#Test the time for evaluating one point
#start_time = time.time()
#loc_vec=np.array([[1,1,0.1]]) #in km
#test=getPointMiniOutage(loc_vec)
#print("--- %s seconds ---" %(time.time() - start_time))
##============VIew the radio map for given height
UAV_height=0.1 # UAV height in km
X_vec=range(D*(step-1)+1)
Y_vec=range(D*(step-1)+1)
numX,numY=np.size(X_vec),np.size(Y_vec)
OutageMapActual=np.zeros(shape=(numX,numY))
Loc_vec_All=np.zeros(shape=(numX*numY,3))
for i in range(numX):
Loc_vec=np.zeros(shape=(numY,3))
Loc_vec[:,0]=X_vec[i]/step
Loc_vec[:,1]=np.array(Y_vec)/step
Loc_vec[:,2]=UAV_height
Loc_vec_All[i*numY:(i+1)*numY,:]=Loc_vec
OutageMapActual[i,:]=getPointMiniOutage(Loc_vec)
# print(OutageMap[i,:])
Outage_vec_All=np.reshape(OutageMapActual,numX*numY)
Test_Size=int(numX*numY/10)
test_indices=random.sample(range(numX*numY),Test_Size)
TEST_LOC_meter=Loc_vec_All[test_indices,:2]*1000
TEST_LOC_ACTUAL_OUTAGE=Outage_vec_All[test_indices]
#fig, ax=plt.subplots()
##plt.style.use('classic')
#cs = ax.contourf(X_vec*10,Y_vec*10,1-OutageMap)
#cbar=fig.colorbar(cs)
#plt.show()
fig=plt.figure(10)
#plt.style.use('classic')
plt.contourf(np.array(X_vec)*10,np.array(Y_vec)*10,1-OutageMapActual)
v = np.linspace(0, 1.0, 11, endpoint=True)
cbar=plt.colorbar(ticks=v)
cbar.set_label('coverage probability',labelpad=20, rotation=270,fontsize=14)
plt.xlabel('x (meter)',fontsize=14)
plt.ylabel('y (meter)',fontsize=14)
plt.show()
fig.savefig('CoverageMapTrue.eps')
fig.savefig('CoverageMapTrue.pdf')
fig.savefig('CoverageMapTrue.jpg')
np.savez('radioenvir',OutageMapActual,X_vec,Y_vec,TEST_LOC_meter,TEST_LOC_ACTUAL_OUTAGE)