Creating temporally and spatially consistent sequences of channel matrices
[1]:
import numpy as np
from neoradium import DeepMimoData, TrjChannel, Carrier, AntennaPanel, random
[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=200, # Number of grid points on trajectory
speedMps=1.2) # Speed in mps (Walking)
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, 69.83, 1.50)
No. of points: 200368
curIdx: 0 (0.00%)
curSpeed: [0.85 0.85 0. ]
Total distance: 240.42 meters
Total time: 200.367 seconds
Average Speed: 1.200 m/s
Carrier Frequency: 3.5 GHz
Paths (Min, Avg, Max): 5, 8.79, 10
Totally blocked: 0
LOS percentage: 59.42%
[3]:
(<Figure size 742.518x471.734 with 1 Axes>,
<Axes: title={'center': 'Map of LOS/NLOS paths'}, xlabel='X', ylabel='Y'>)
[4]:
# Create a MIMO channel model based on our trajectory.
channel = TrjChannel(bwp, trajectory,
txAntenna = AntennaPanel([2,4], polarization="x"), # 8 TX antenna
txOrientation = [180,0,0], # Facing to the left
rxAntenna = AntennaPanel([1,2], polarization="x")) # 2 RX antenna
print(channel)
TrjChannel Properties:
carrierFreq: 3.5 GHz
normalizeGains: True
normalizeOutput: True
normalizeDelays: True
interpolateSlots: True
xPolPower: 10.00 (dB)
filterLen: 16 samples
delayQuantSize: 64
stopBandAtten: 80 dB
dopplerShift: 14.015351581688819 Hz
coherenceTime: 0.03019133592150883 sec
TX Antenna:
Total Elements: 16
spacing: 0.5𝜆, 0.5𝜆
shape: 2 rows x 4 columns
polarization: x
Orientation (𝛼,𝛃,𝛄): 180° 0° 0°
RX Antenna:
Total Elements: 4
spacing: 0.5𝜆, 0.5𝜆
shape: 1 rows x 2 columns
polarization: x
Trajectory:
start (x,y,z): (-164.55, 69.83, 1.50)
No. of points: 200368
curIdx: 0 (0.00%)
curSpeed: [0.85 0.85 0. ]
Total distance: 240.42 meters
Total time: 200.367 seconds
Average Speed: 1.200 m/s
Carrier Frequency: 3.5 GHz
Paths (Min, Avg, Max): 5, 8.79, 10
Totally blocked: 0
LOS percentage: 59.42%
[5]:
# Now create a sequence generator that generates up to 20 sequences with sequences
# of length 10, containing every other slot.
# Trajectory Points: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...
# Channels in the sequence: 0 1 2 3 4 5 6 7 8 9 0 1 ...
# Sequence Number: 0 0 0 0 0 0 0 0 0 0 1 1 ...
chanSeqGen = channel.getChanSeqGen(seqPeriod=2, seqLen=10, maxNumSeq=20)
[6]:
chanSeqGen.reset()
for i, chanSeq in enumerate(chanSeqGen):
print(f"Shape of Sequence {i}: {chanSeq.shape}")
Shape of Sequence 0: (10, 14, 300, 4, 16)
Shape of Sequence 1: (10, 14, 300, 4, 16)
Shape of Sequence 2: (10, 14, 300, 4, 16)
Shape of Sequence 3: (10, 14, 300, 4, 16)
Shape of Sequence 4: (10, 14, 300, 4, 16)
Shape of Sequence 5: (10, 14, 300, 4, 16)
Shape of Sequence 6: (10, 14, 300, 4, 16)
Shape of Sequence 7: (10, 14, 300, 4, 16)
Shape of Sequence 8: (10, 14, 300, 4, 16)
Shape of Sequence 9: (10, 14, 300, 4, 16)
Shape of Sequence 10: (10, 14, 300, 4, 16)
Shape of Sequence 11: (10, 14, 300, 4, 16)
Shape of Sequence 12: (10, 14, 300, 4, 16)
Shape of Sequence 13: (10, 14, 300, 4, 16)
Shape of Sequence 14: (10, 14, 300, 4, 16)
Shape of Sequence 15: (10, 14, 300, 4, 16)
Shape of Sequence 16: (10, 14, 300, 4, 16)
Shape of Sequence 17: (10, 14, 300, 4, 16)
Shape of Sequence 18: (10, 14, 300, 4, 16)
Shape of Sequence 19: (10, 14, 300, 4, 16)
[7]:
# The following example uses the callback functions to modify the
from neoradium import Carrier, PDSCH
carrier = Carrier(numRbs=25, spacing=30) # Carrier with 25 RBs, 30KHz subcarrier spacing
channel = TrjChannel(carrier.curBwp, trajectory,
txAntenna = AntennaPanel([2,4], polarization="x"), # 8 TX antenna
rxAntenna = AntennaPanel([1,2], polarization="x")) # 2 RX antenna
pdsch = PDSCH(carrier.curBwp, numLayers=2, nID=carrier.cellId, modulation="16QAM")
def chanCallback(seqNo, elementNo, channelMatrix, channel):
# Drop the channels with total blockage
return None if channel.totalBlockage else channelMatrix
def seqCallback(seqNo, channelSeq, channel):
# Get the precoder matrix based on the first channel in the sequence
precoder = pdsch.getPrecodingMatrix(channelSeq[0])
# Now create and return a new sequence containing the effective channels by applying
# the precoder to all channels in the sequence
newSeq = np.stack([channel.getEffChannel(chanMat, precoder) for chanMat in channelSeq])
return newSeq
chanSeqGen = channel.getChanSeqGen(seqPeriod=1, seqLen=5, maxNumSeq=20,
chanCallback=chanCallback, seqCallback=seqCallback)
channelSequences = np.stack([chanSeq for chanSeq in chanSeqGen])
print(channelSequences.shape) # Prints: (20, 5, 14, 300, 4, 2) for (numSeq, seqLen, L, K, Nr, Nt)
(20, 5, 14, 300, 4, 2)
[ ]:
[ ]: