Generating the Wireless Channel for Mobile Users
Objective
Generate Channel for Mobile users
Variation in power with time.
In this tutorial, we will learn how to generate a channel for mobile users and analyze how the power received by users moving vary over time.
To set up a simulation, we consider a layout having a 3 sector Hexagonal geometry, where the Base Stations are located at the center of hexagon covering each sector and a single User Equipment (UE) moving on a circular trajectory.
We choose RuralMacro (RMa) terrain with a carrier frequency of 3 GHz for simulation.
We also choose omni directional dipole antenna for Receiver (Rx) and a parabolic antenna for Transmitter (Tx).
We first import the necessary libraries followed by creating objects of classes
AntennaArrays,NodeMobility, andSimulationLayoutrespectively
The content of the tutorial is as follows:
Table Of Content
Import Libraries
Python Libraries
[1]:
import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
%matplotlib widget
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.animation as animation
import numpy as np
5G Toolkit Libraries
[2]:
from toolkit5G.ChannelModels import NodeMobility
from toolkit5G.ChannelModels import AntennaArrays
from toolkit5G.ChannelModels import SimulationLayout
from toolkit5G.ChannelModels import ParameterGenerator
from toolkit5G.ChannelModels import ChannelGenerator
[3]:
# from IPython.display import display, HTML
# display(HTML("<style>.container { width:100% !important; }</style>"))
Simulation Parameters
Define the following Simulation Parameters:
propTerraindefines propagation terrain for BS-UE linkscarrierFrequencydefines carrier frequency in HznBSsdefines number of Base Stations (BSs)nUEsdefines number of User Equipments (UEs)nSnapShotsdefines number of SnapShots, where SnapShots correspond to different time-instants at which a mobile user channel is being generated.
[4]:
# Simulation Parameters
propTerrain = "RMa" # Propagation Scenario or Terrain for BS-UE links
carrierFrequency = 3*10**9 # carrier frequency in Hz
nBSs = 3 # number of BSs
nUEs = 1 # number of UEs
nSnapShots = 60 # number of SnapShots
Antenna Arrays
Antenna Array at Rx
The following steps describe the procedure to generate AntennaArrays Objects at a single carrier frequency both at Tx and Rx side:
Choose an omni directional dipole antenna for Rx, for which we have to pass the string “OMNI” while instantiating
AntennaArraysclass.Pass
arrayStructureof[1,1,2,2,1]meaning 1 panel in vertical direction, 1 panel in horizonatal direction, 2 antenna elements per column per panel, 2 columns per panel and 1 correspond to antenna element being single polarized.For this antenna structure, the number of Rx antennas
Nrto be 4.
[5]:
# Antenna Array at UE side
# antenna element type to be "OMNI"
# with single panel and 4 single polarized antenna element per panel.
ueAntArray = AntennaArrays(antennaType = "OMNI",
centerFrequency = carrierFrequency,
arrayStructure = np.array([1,1,2,2,1]))
ueAntArray()
# num of Rx antenna elements
nr = ueAntArray.numAntennas
# Radiation Pattern of Rx antenna element
ueAntArray.displayAntennaRadiationPattern()
Antenna Array at Tx
We choose a parabolic antenna for Tx, for which we have to pass the string
"3GPP_38.901"while instantiatingAntennaArraysclass.We pass
arrayStructureof[1,1,2,4,2]meaning 1 panel in vertical direction, 1 panel in horizonatal direction, 2 antenna elements per column per panel, 4 columns per panel and 2 correspond to antenna element being dual polarized.With this structure, we obtain number of Tx antennas
ntto be 16.
[6]:
# Antenna Array at BS side
# antenna element type to be "3GPP_38.901", a parabolic antenna
# with single panel and 8 dual polarized antenna element per panel.
bsAntArray = AntennaArrays(antennaType = "3GPP_38.901",
centerFrequency = carrierFrequency,
arrayStructure = np.array([1,1,2,4,2]))
bsAntArray()
# num of Tx antenna elements
nt = bsAntArray.numAntennas
# Radiation Pattern of Tx antenna element
bsAntArray.displayAntennaRadiationPattern()
Node Mobility
Generate the route/trajectory for the mobile UE:
All the Base Stations (BSs) are considered to be static and the User Equipments (UE) is mobile.
The UE is moving at 0.833 m/s (3 kmph) on a circular trajectory of radius 250 meter centered around origin.
For the UE, 60 snapshots are drawn while in motion on the circle with an interval of 5 sec.
The parameters are selected such that the UE complete the circumference of the circle.
[7]:
# NodeMobility parameters
# assuming that all the BSs are static and all the UEs are mobile.
# time values at each snapshot.
isInitLocationRandom = True # Initial location of the UE is random.
initAngle = None # Not required when isInitLocationRandom is True.
isInitOrientationRandom = False # UE Orientations are UE. Not randomized.
snapshotInterval = 5 # 5 second
speed = 0.833 # speed of the UE 3 Kmph
radius = 250 # 3 Kmph
timeInst = snapshotInterval*np.arange(nSnapShots, dtype=np.float32)
UEroute = NodeMobility("circular", nUEs, timeInst, radius, radius,
speed, speed, isInitLocationRandom, initAngle,
isInitOrientationRandom)
UEroute()
fig, ax = UEroute.displayRoute()
ax.set_aspect(True)
Simulation Layout
We define the simulation topology parametes:
ISD: Inter Site DistanceminDist: Minimum distance between transmitter and receiver.bsHt: BS heightsueHt: UE heightstopology: Simulation TopologynSectorsPerSite: Number of Sectors Per Site
Furthermore, users can access and update following parameters as per their requirements for channel using the handle simLayoutObj.x where x is:
The following parameters can be accessed or updated immendiately after object creation
UEtracksUELocationsueOrientationUEvelocityVectorBStracksBSLocationsbsOrientationBSvelocityVector
The following parameters can be accessed or updated after calling the object
linkStateVec
[8]:
# Layout Parameters
isd = 500 # inter site distance
minDist = 35 # min distance between each UE and BS
ueHt = 1.5 # UE height
bsHt = 35 # BS height
topology = "Hexagonal" # BS layout type
nSectorsPerSite = 3 # number of sectors per site
# simulation layout object
simLayoutObj = SimulationLayout(numOfBS = nBSs,
numOfUE = nUEs,
heightOfBS = bsHt,
heightOfUE = ueHt,
ISD = isd,
layoutType = topology,
numOfSectorsPerSite = nSectorsPerSite,
ueRoute = UEroute)
# Update UE location for motion over a circle centered around the BS location.
simLayoutObj.UELocations = -simLayoutObj.UEtracks.mean(0)
simLayoutObj(terrain = propTerrain,
carrierFreq = carrierFrequency,
ueAntennaArray = ueAntArray,
bsAntennaArray = bsAntArray,
forceLOS = True)
# displaying the topology of simulation layout
fig, ax = simLayoutObj.display2DTopology()
ax.scatter(simLayoutObj.UELocations[0,0]+simLayoutObj.UEtracks[:,0,0],
simLayoutObj.UELocations[0,1]+simLayoutObj.UEtracks[:,0,1], color="k", zorder=-1)
ax.scatter(simLayoutObj.UELocations[0,0],simLayoutObj.UELocations[0,1], color="b", label = "UE-InitialLocation", zorder=-1)
ax.set_xlabel("x-coordinates (m)")
ax.set_ylabel("y-coordinates (m)")
ax.set_title("Simulation Topology")
ax.legend()
# plt.show()
[8]:
<matplotlib.legend.Legend at 0x7f49e6a5ff50>
Channel Parameters, Channel Coefficients and OFDM Channel
The UE can access the channel coefficents and other parameters using following handles:
LSPs/SSPs: paramGenObj.x where x is
linkStateVecdelaySpreadphiAoA_LoS,phiAoA_mn,phiAoA_spreadthetaAoA_LoS,thetaAoA_mn,thetaAoA_spreadphiAoD_LoS,phiAoD_mn,phiAoD_spreadthetaAoD_LoS,thetaAoD_mn,thetaAoD_spreadxprpathloss,pathDelay,pathPowershadowFading
Channel Co-efficeints: channel.x where x is
coefficientsdelays
Shape of OFDM Channel:
Hfis of shape :(number of carrier frequencies, number of snapshots, number of BSs, number of UEs, Nfft, number of Rx antennas, number of Tx antennas)
[9]:
# Generate SSPs/LSPs Parameters:
paramGenObj = simLayoutObj.getParameterGenerator()
# Generate Channel Coefficeints and Delays: SSPs/LSPs
channel = paramGenObj.getChannel(applyPathLoss = True)
# Channel coefficients can be accessed using: channel.coefficients
# Channel delays can be accessed using: channel.delays
# Generate OFDM Channel
Nfft = 1024
Hf = channel.ofdm(30*10**3, Nfft, simLayoutObj.carrierFrequency)
[Warning]: UE height 'hUE' cannot be less than 1! These values are forced to 1!
dBP (min, max): 2199.114990234375, 2199.114990234375
[10]:
Hf.shape
[10]:
(1, 60, 3, 1, 1024, 4, 16)
Variation in Channel Power across Time
The following code snippets displays the variation of received power of a UE when moves on a circular track (centered around origin) starting from its
initial position.In the current simulation we have 3 BSs and 1 UE moving on a circular track starting from a random intitial position inside a hexagonal layout.
[11]:
fig, ax = plt.subplots()
power = 10*np.log10(((np.abs(Hf)**2).sum(axis=0).sum(axis=2).sum(axis=2).sum(axis=2).sum(axis=2))/(nr*nt))
colors = np.array(['palegreen', 'crimson','royalblue'])
ax.plot(timeInst, power[:,0], colors[0], label = "BS-0")
ax.plot(timeInst, power[:,1], colors[1], label = "BS-1")
ax.plot(timeInst, power[:,2], colors[2], label = "BS-2")
ax.legend()
ax.grid()
ax.set_xlabel('Time Instances (sec)')
ax.set_ylabel('Received-Power (dB)')
ax.set_title('Received-Power Variation With Time', fontsize=12)
plt.show()
Animation
Functions to Animate the Plot
[12]:
def wrapTo30(ang):
# Function to wrap angles not exceeding 30 degree.
ang = np.mod(ang, np.pi/3)
return np.where(ang>np.pi/6, ang-np.pi/3, ang)
def plotLayout(ax):
scale = 8
colors = np.array(['palegreen', 'crimson', 'royalblue', 'gold', 'midnightblue', 'purple','orange','lightcoral'])
delAngle = 360/nSectorsPerSite
numSites = int(nBSs/nSectorsPerSite)
simLayoutObj.BSLocations
mark = ['c--', 'm:', 'y-']
# Add some coloured hexagons
for idx in range(numSites):
# color = c[0]
hex = patches.RegularPolygon((simLayoutObj.BSLocations[0,idx], simLayoutObj.BSLocations[1,idx]), numVertices=6,
radius=isd/np.sqrt(3),orientation=np.radians(120),
facecolor = 'none',alpha=1, edgecolor='k', lw = 1.75)
ax.add_patch(hex)
# Also add a text label
for n in range(nSectorsPerSite):
sector = patches.Wedge((simLayoutObj.BSLocations[0,idx], simLayoutObj.BSLocations[1,idx]), # (x,y)
isd/scale, # radius
n*delAngle, # theta1 (in degrees)
(n+1)*delAngle, # theta2
color=colors[n%8],
alpha=1)
ax.add_patch(sector)
if(nSectorsPerSite != 1):
boundDistance = isd*np.sqrt(5/12-(1/6)*np.abs(np.cos(2*wrapTo30(n*delAngle*np.pi/180))))
ax.plot([simLayoutObj.BSLocations[0,idx], simLayoutObj.BSLocations[0,idx] + boundDistance*np.cos(n*delAngle*np.pi/180)],
[simLayoutObj.BSLocations[1,idx], simLayoutObj.BSLocations[1,idx] + boundDistance*np.sin(n*delAngle*np.pi/180)],
mark[n%3], lw=2, label = "Sector "+ str(n) + "-->"+str((n+1)%3) + " Boundary")
# function that draws each frame of the animation
def animate(i):
x.append(timeInst[i])
y0.append(power[:,0][i])
y1.append(power[:,1][i])
y2.append(power[:,2][i])
ax[0].clear()
ax[0].grid()
ax[0].plot(x, y0, color='palegreen')
ax[0].plot(x, y1, color='crimson')
ax[0].plot(x, y2, color='royalblue')
ax[0].set_xlim([0, timeInst[-1]])
ax[0].set_ylim([-100, 10])
# ax[0].margins(x=0, y=-0.25) # Values in (-0.5, 0.0) zooms in to center
ax[0].scatter(timeInst[i], power[:,0][i], color ='palegreen', label = "Received-Power from BS-0")
ax[0].scatter(timeInst[i], power[:,1][i], color ='crimson', label = "Received-Power from BS-1")
ax[0].scatter(timeInst[i], power[:,2][i], color ='royalblue', label = "Received-Power from BS-2")
ax[0].axvline(x = timeInst[10], color ='c', ls = "--", lw = 2, label = "Sector 0-->1 Boundary")
ax[0].axvline(x = timeInst[30], color ='m', ls = ":", lw = 2, label = "Sector 1-->2 Boundary")
ax[0].axvline(x = timeInst[50], color ='y', lw = 2, label = "Sector 2-->0 Boundary")
ax[0].set_xlabel('Time Instances (sec)')
ax[0].set_ylabel('Received-Power (dB)')
ax[0].set_title('Received-Power Variation With Time', fontsize=12)
ax[0].legend()
ax[1].clear()
ax[1].grid()
ax[1].scatter(simLayoutObj.UELocations[0,0]+simLayoutObj.UEtracks[0:i,0,0],
simLayoutObj.UELocations[0,1]+simLayoutObj.UEtracks[0:i,0,1], color="k", zorder=-1, label = "UE's Past Locations")
ax[1].scatter(simLayoutObj.UELocations[0,0]+simLayoutObj.UEtracks[i,0,0],
simLayoutObj.UELocations[0,1]+simLayoutObj.UEtracks[i,0,1], color="r", zorder=-1, label = "UE's Current Locations")
ax[1].scatter(simLayoutObj.UELocations[0,0],simLayoutObj.UELocations[0,1], color="b", zorder=-1, label = "UE's Start Loaction")
plotLayout(ax[1])
ax[1].set_xlabel('x-coordinates (m)')
ax[1].set_ylabel('y-coordinates (m)')
ax[1].set_title('Simulation Layout', fontsize=12)
ax[1].legend()
ax[1].set_xlim([-300, 300])
ax[1].set_ylim([-300, 300])
# ax[1].margins(x=0, y=-0.25) # Values in (-0.5, 0.0) zooms in to center
Simulation Animation
[13]:
# create the figure and axes objects
scaleFig = 1.75
fig, ax = plt.subplots(1,2,figsize=(17.5/scaleFig,7.5/scaleFig))
fig.suptitle('Simulation of Node Mobility', fontsize=10)
# ax[0].set_aspect('equal')
# ax[1].set_aspect('equal')
# create empty lists for the x and y data
x = []
y0 = []
y1 = []
y2 = []
#####################
# run the animation
#####################
# frames= 20 means 20 times the animation function is called.
# interval=500 means 500 milliseconds between each frame.
# repeat=False means that after all the frames are drawn, the animation will not repeat.
# Note: plt.show() line is always called after the FuncAnimation line.
anim = animation.FuncAnimation(fig, animate, frames=timeInst.shape[0], interval=100, repeat=False, blit=True)
# saving to mp4 using ffmpeg writer
# writervideo = animation.FFMpegWriter(fps=30)
# anim.save('SimulationOfNodeMobility.mp4', writer=writervideo)
# anim.save('SimulationOfNodeMobility.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()
Further Study
Simulate by increasing number of UEs
nUEsgreater than 1 and see how the power varies with time for each mobile user.Increase number of carrier frequencies to be grater than 1 and see how carrier frequency effects the power.
Simulate the same channel for
NLOSlinks as well by makingforceLOS = Falseand see how the performance change.
[ ]: