TDL Channel

This notebook shows how to use a TDL channel in time and frequency domains.

[1]:
import numpy as np
import scipy.io
import time

from neoradium import Carrier, Modem, TdlChannel, Grid, random
from neoradium.utils import getNmse
[2]:
# Create a valid random grid
random.setSeed(123)                                    # Make results reproducible
carrier = Carrier(startRb=0, numRbs=25, spacing=15)    # Carrier 25 Resource Blocks, 15KHz subcarrier spacing
bwp = carrier.curBwp                                   # The only bandwidth part in the carrier
txGrid = bwp.createGrid(numPlanes=8)                   # 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: (8, 14, 300)
Shape of Waveform:      (8, 30720)
[3]:
# Create a TDL-D channel model with 4GHz carrier frequency, and 50Hz doppler shift
channel = TdlChannel(bwp, 'D', carrierFreq=4e9, dopplerShift=50,
                     txAntennaCount=8, rxAntennaCount=2, mimoCorrelation='Medium')
print(channel)

TDL-D Channel Properties:
  carrierFreq:      4 GHz
  normalizeGains:   True
  normalizeOutput:  True
  txDir:            Downlink
  filterLen:        16 samples
  delayQuantSize:   64
  stopBandAtten:    80 db
  dopplerShift:     50 Hz
  coherenceTime:    8.463 milliseconds
  delaySpread:      30 ns
  pathDelays (ns):  0.0000 1.0500 18.360 40.890 42.150 54.120 77.880 53.250 121.26 238.11
                    282.72 291.24 375.75
  pathPowers (db):  -0.001 -18.80 -21.00 -22.80 -17.90 -20.10 -21.90 -22.90 -27.80 -23.60
                    -24.80 -30.00 -27.70
  hasLOS:           True
  kFactorLos (db):  13.300
  rxAntennaCount:   2
  txAntennaCount:   8
  mimoCorrelation:  Medium
  polarization:     CoPolar
  sosType:          GMEDS1
  sosNumSins:       32

[4]:
# 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.004817008972167969
[5]:
# 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.09486007690429688
NMSE between the rxGrid in Time and Freq. domains:  6.26924445685763e-17
[ ]: