Using the Trajectory-based Channel Model

[1]:
import numpy as np
import os, time
import scipy
import matplotlib.pyplot as plt

from neoradium import DeepMimoData, TrjChannel, Carrier, Grid, Modem, AntennaPanel, random
from neoradium.utils import getNmse
[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, 39.83, 1.50)
  No. of points:         201752
  curIdx:                0 (0.00%)
  curSpeed:              [0.85 0.85 0.  ]
  Total distance:        242.08 meters
  Total time:            201.751 seconds
  Average Speed:         1.200 mps
  Carrier Frequency:     3.5 GHz
  Paths (Min, Avg, Max): 1, 8.97, 10
  Totally blocked:       0
  LOS percentage:        32.84%

[3]:
(<Figure size 742.518x471.734 with 1 Axes>,
 <Axes: title={'center': 'Map of LOS/NLOS paths'}, xlabel='X', ylabel='Y'>)
../../../../_images/source_Playground_Notebooks_RayTracing_TrajChannel_3_2.png
[ ]:

[4]:
# Create a random grid
txGrid = bwp.createGrid(numPlanes=16)                   # Create an empty resource grid

stats = txGrid.getStats()                              # Get statistics about the grid
modem = Modem("16QAM")                                 # Using 16QAM modulation
numRandomBits = stats['UNASSIGNED']*modem.qm           # Total number of bits available in the resource grid

bits = random.bits(numRandomBits)                      # Create random bits
symbols = modem.modulate(bits)                         # Modulate the bits to get symbols

indexes = txGrid.getReIndexes("UNASSIGNED")            # Indexes of the "UNASSIGNED" resources
txGrid[indexes] = symbols                              # Put symbols in the resource grid

txWaveform = txGrid.ofdmModulate()                     # OFDM-Modulate the resource grid to get a waveform

print("Shape of Resource Grid:",txGrid.shape)
print("Shape of Waveform:     ",txWaveform.shape)
Shape of Resource Grid: (16, 14, 300)
Shape of Waveform:      (16, 30720)
[5]:
# 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 left
                     rxAntenna = AntennaPanel([1,2], polarization="x"))       # 2 RX antenna
print(channel)

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:         14.015298117569412 Hz
  coherenceTime:        0.030191451092315417 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, 39.83, 1.50)
    No. of points:         201752
    curIdx:                0 (0.00%)
    curSpeed:              [0.85 0.85 0.  ]
    Total distance:        242.08 meters
    Total time:            201.751 seconds
    Average Speed:         1.200 mps
    Carrier Frequency:     3.5 GHz
    Paths (Min, Avg, Max): 1, 8.97, 10
    Totally blocked:       0
    LOS percentage:        32.84%

[6]:
# Apply the channel in Frequency Domain:
t0 =time.time()
channelMatrix = channel.getChannelMatrix()
rxGridF = txGrid.applyChannel(channelMatrix)
t1 =time.time()
print("Time to apply channel in Freq. Domain:", t1-t0)
Time to apply channel in Freq. Domain: 0.013160228729248047
[7]:
# Applying the channel in time domain and demodulate to get a received resource grid (rxGrid)
t0 =time.time()
maxDelay = channel.getMaxDelay()                         # Calculate the channel's max delay
paddedTxWaveform = txWaveform.pad(maxDelay)              # Pad the waveform with zeros
rxWaveform = channel.applyToSignal(paddedTxWaveform)     # Apply the channel to the waveform
offset = channel.getTimingOffset()                       # Get the timing offset for synchronization
syncedWaveform = rxWaveform.sync(offset)                 # Synchronization
rxGridT = Grid.ofdmDemodulate(bwp, syncedWaveform)       # OFDM-demodulation
t1 =time.time()
print("Time to apply channel in Time Domain:", t1-t0)
print("NMSE between the rxGrid in Time and Freq. domains: ",getNmse(rxGridT.grid,rxGridF.grid))

Time to apply channel in Time Domain: 0.2824540138244629
NMSE between the rxGrid in Time and Freq. domains:  0.0005440608093966191
[ ]: