Studying Bit Error Rate changes while moving along a trajectory
[1]:
import numpy as np
import os, time
import scipy
import matplotlib.pyplot as plt
import matplotlib
from IPython.display import HTML
from neoradium import DeepMimoData, TrjChannel, Carrier, Grid, AntennaPanel, random, PDSCH
from neoradium.utils import toDb, toLinear
[2]:
# Replace this with the folder on your computer where you store DeepMIMO scenarios
dataFolder = "/data/RayTracing/DeepMIMO/Scenarios/V4/"
DeepMimoData.setScenariosPath(dataFolder)
# Create a DeepMimoData object
deepMimoData = DeepMimoData("asu_campus_3p5")
deepMimoData.print()
DeepMimoData Properties:
Scenario: asu_campus_3p5
Version: 4.0.0a3
UE Grid: rx_grid
Grid Size: 411 x 321
Base Station: BS (at [166. 104. 22.])
Total Grid Points: 131,931
UE Spacing: [1. 1.]
UE bounds (xyMin, xyMax) [-225.55 -160.17], [184.45 159.83]
UE Height: 1.50
Carrier Frequency: 3.5 GHz
Num. paths (Min, Avg, Max): 0, 6.21, 10
Num. total blockage: 46774
LOS percentage: 19.71%
[3]:
random.setSeed(123) # Make results reproducible
# Create the carrier:
carrier = Carrier(startRb=0, numRbs=25, spacing=15) # Carrier with 25 Resource Blocks, 15KHz subcarrier spacing
bwp = carrier.curBwp # The only bandwidth part in the carrier
# Create a random trajectory at waking speed.
trajectory = deepMimoData.getRandomTrajectory(xyBounds=np.array([[-210, 40], [-120, 100]]), # Traj. bounds
segLen=5, # Num grid points on shortest segment
bwp=bwp, # The bandwidth part
trajLen=100, # Number of grid points on trajectory
speedMps=15) # Speed in mps
trajectory.print() # Print the trajectory information
deepMimoData.drawMap("LOS-NLOS", trajectory) # Draw the Map with the trajectory
Trajectory Properties:
start (x,y,z): (-164.55, 39.83, 1.50)
No. of points: 8227
curIdx: 0 (0.00%)
curSpeed: [10.64 10.64 0. ]
Total distance: 123.44 meters
Total time: 8.226 seconds
Average Speed: 15.006 m/s
Carrier Frequency: 3.5 GHz
Paths (Min, Avg, Max): 6, 8.99, 10
Totally blocked: 0
LOS percentage: 26.43%
[3]:
(<Figure size 742.518x471.734 with 1 Axes>,
<Axes: title={'center': 'Map of LOS/NLOS paths'}, xlabel='X', ylabel='Y'>)

[4]:
# Create a PDSCH object
pdsch = PDSCH(bwp, interleavingBundleSize=0, numLayers=1, nID=carrier.cellId, modulation="64QAM")
pdsch.setDMRS(prgSize=0, configType=2, additionalPos=2) # Specify the DMRS configuration
channel = TrjChannel(bwp, trajectory,
txAntenna = AntennaPanel([2,4]), # 8 TX antenna
txOrientation = [180,0,0], # TX antenna pointing to the left
rxAntenna = AntennaPanel([1,2]), # 2 RX antenna
seed = 123)
print(channel)
snrDbs = [-20,0]
chanEstMethods = ["Perfect", "LS"]
prevBer = {f"{chanEstMethod}{snrDb}": 0 for chanEstMethod in chanEstMethods for snrDb in snrDbs}
def getBER(channel, snrDbs, end):
berInfo = {f"{chanEstMethod}{snrDb}": np.int32([0,0]) for chanEstMethod in chanEstMethods for snrDb in snrDbs}
while channel.trajectory.curIdx < end:
channelMatrix = channel.getChannelMatrix() # Get the channel matrix
precoder = pdsch.getPrecodingMatrix(channelMatrix) # Get the precoder matrix from PDSCH object
for snrDb in snrDbs:
for chanEstMethod in chanEstMethods:
grid = pdsch.getGrid() # Create a resource grid already populated with DMRS
numBits = pdsch.getBitSizes(grid)[0] # Actual number of bits available in the resource grid
txBits = random.bits(numBits) # Create random binary data
# Now populate the resource grid with coded data. This includes QAM modulation and resource mapping.
pdsch.populateGrid(grid, txBits)
# Store the indexes of the PDSCH data in pdschIndexes to be used later.
pdschIndexes = pdsch.getReIndexes(grid, "PDSCH")
precodedGrid = grid.precode(precoder) # Perform the precoding
rxGrid = precodedGrid.applyChannel(channelMatrix) # Apply the channel in frequency domain
rxGrid = rxGrid.addNoise(snrDb=snrDb) # Add noise
if chanEstMethod == "Perfect": # Perfect Channel Estimation
estChannelMatrix = channelMatrix @ precoder[None,...]
else: # LS + Interpolation Channel Estimation
estChannelMatrix, noiseEst = rxGrid.estimateChannelLS(pdsch.dmrs, polarInt=False, kernel='linear')
eqGrid, llrScales = rxGrid.equalize(estChannelMatrix) # Equalization
rxBits = pdsch.getHardBitsFromGrid(eqGrid, pdschIndexes)[0] # Demodulation
bitErrors = np.abs(rxBits-txBits).sum() # Calculating number of bit errors
berInfo[f"{chanEstMethod}{snrDb}"] += [bitErrors, numBits]
channel.goNext()
for chanEstMethod in chanEstMethods:
for snrDb in snrDbs:
k = f"{chanEstMethod}{snrDb}"
berInfo[k] = berInfo[k][0]/berInfo[k][1]
return berInfo
# A call back function that is called to draw up to three graphs below the animated trajectory
def handleGraph(request, ax, trajectory, points=None):
if request=="Config":
# Configure all graphs
if len(ax)>0:
ax[0].set_xlim(0,trajectory.numPoints)
ax[0].set_ylim(0,.5)
ax[0].set_title("BER (LS)")
ax[0].grid()
if len(ax)>1:
ax[1].set_xlim(0,trajectory.numPoints)
ax[1].set_ylim(0,.5)
ax[1].set_title("BER (Perfect)")
ax[1].grid()
elif request=="Draw":
# For Draw: ax is an array of `numGraphs` elements
p0, p1 = points
berInfo = getBER(channel, snrDbs, p1)
if len(ax)>0:
for i, snrDb in enumerate(snrDbs):
k = f"LS{snrDb}"
ax[0].plot([p0,p1], [prevBer[k], berInfo[k]], ['orange','red'][i], markersize=1, label=f"SNR: {snrDb} dB")
prevBer[k] = berInfo[k]
if p0==0: ax[0].legend(fontsize=8)
if len(ax)>1:
for i, snrDb in enumerate(snrDbs):
k = f"Perfect{snrDb}"
ax[1].plot([p0,p1], [prevBer[k], berInfo[k]], ['blue','black'][i], markersize=1, label=f"SNR: {snrDb} dB")
prevBer[k] = berInfo[k]
if p0==0: ax[1].legend(fontsize=8)
print("\r Current Trajectory Point: %d (%d%%) "%(trajectory.curIdx,
trajectory.curIdx*100//trajectory.numPoints), end="")
TrjChannel Properties:
carrierFreq: 3.5 GHz
normalizeGains: True
normalizeOutput: True
normalizeDelays: True
xPolPower: 10.00 (dB)
filterLen: 16 samples
delayQuantSize: 64
stopBandAtten: 80 dB
dopplerShift: 175.6448069011776 Hz
coherenceTime: 0.0024090788399960396 sec
TX Antenna:
Total Elements: 8
spacing: 0.5𝜆, 0.5𝜆
shape: 2 rows x 4 columns
polarization: |
Orientation (𝛼,𝛃,𝛄): 180° 0° 0°
RX Antenna:
Total Elements: 2
spacing: 0.5𝜆, 0.5𝜆
shape: 1 rows x 2 columns
polarization: |
Trajectory:
start (x,y,z): (-164.55, 39.83, 1.50)
No. of points: 8227
curIdx: 0 (0.00%)
curSpeed: [10.64 10.64 0. ]
Total distance: 123.44 meters
Total time: 8.226 seconds
Average Speed: 15.006 m/s
Carrier Frequency: 3.5 GHz
Paths (Min, Avg, Max): 6, 8.99, 10
Totally blocked: 0
LOS percentage: 26.43%
[5]:
# Increase the animation memory limit to 100 MB
matplotlib.rcParams['animation.embed_limit'] = 100000000
# Now create the animation and display it in the following cell. This can take a while...
channel.restart()
anim = deepMimoData.animateTrajectory(trajectory, numGraphs=2, pointsPerFrame=20, graphCallback=handleGraph)
HTML(anim.to_jshtml())
Current Trajectory Point: 8200 (99%)
[5]: