Channel timing

This notebooks shows the timing characteristics of a CDL channel model as it is applied to input signals for a duration of 100 slots.

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

from neoradium import Carrier, Modem, CdlChannel, AntennaPanel, Grid, Waveform, random
from neoradium.utils import getNmse, getMse

[2]:
# Create a random grid and the corresponding TX waveform
carrierFreq=4e9 # 4 GHz

carrier = Carrier(startRb=0, numRbs=25, spacing=30) # Also try other subcarrier spacing values
bwp = carrier.curBwp
txGrid = bwp.createGrid(numPlanes=8)

stats = txGrid.getStats()
modem = Modem("16QAM")
numRandomBits = stats['UNASSIGNED']*modem.qm

bits = random.bits(numRandomBits)
symbols = modem.modulate(bits)

indexes = txGrid.getReIndexes("UNASSIGNED")
txGrid[indexes] = symbols

txWaveform = txGrid.ofdmModulate(carrierFreq)
print(bwp)
print("Shape of transmitted signal in time domain:",txWaveform.shape)

Bandwidth Part Properties:
  Resource Blocks:    25 RBs starting at 0 (300 subcarriers)
  Subcarrier Spacing: 30 KHz
  CP Type:            normal
  bandwidth:          9 MHz
  symbolsPerSlot:     14
  slotsPerSubFrame:   2
  nFFT:               1024
  frameNo:            0
  slotNo:             0

Shape of transmitted signal in time domain: (8, 15360)
[3]:
# No Channel and no noise performance: ofdmModulate -> ofdmDemodulate
rxGridTD = txWaveform.ofdmDemodulate(bwp, f0=carrierFreq)
print(f"NMSE: {getNmse(txGrid.grid,rxGridTD.grid)}")
print(f"MAE: {np.abs(txGrid.grid-rxGridTD.grid).max()}")
NMSE: 4.011172718876436e-32
MAE: 8.005932084973443e-16
[4]:
# Create a CDL-D channel
channel = CdlChannel(bwp, 'D', delaySpread=300, carrierFreq=4e9, dopplerShift=15,
                     txAntenna = AntennaPanel([2,4], polarization="|"),  # 8 TX antenna
                     rxAntenna = AntennaPanel([1,2], polarization="|"),  # 2 RX antenna
                     seed = 1234)
channel
[4]:

CDL-D Channel Properties:
  carrierFreq:          4 GHz
  normalizeGains:       True
  normalizeOutput:      True
  txDir:                Downlink
  filterLen:            16 samples
  delayQuantSize:       64
  stopBandAtten:        80 db
  dopplerShift:         15 Hz
  coherenceTime:        0.028209479177387815 Sec.
  delaySpread:          300 ns
  ueDirAZ:              0.0°, 90.0°
  Cross Pol. Power:     11 db
  angleSpreads:         5° 8° 3° 3°
  TX Antenna:
    Total Elements:     8
    spacing:            0.5𝜆, 0.5𝜆
    shape:              2 rows x 4 columns
    polarization:       |
    Orientation (𝛼,𝛃,𝛄): 0° 0° 0°
  RX Antenna:
    Total Elements:     2
    spacing:            0.5𝜆, 0.5𝜆
    shape:              1 rows x 2 columns
    polarization:       |
    Orientation (𝛼,𝛃,𝛄): 0° 0° 0°
  hasLOS:               True
  LOS Path:
    Delay (ns):         0.00000
    Power (db):         -0.20000
    AOD (Deg):          0
    AOA (Deg):          -3
    ZOD (Deg):          1
    ZOA (Deg):          1
  NLOS Paths (13):
    Delays (ns):        0.000 10.50 183.6 408.9 421.5 541.2 778.8 532.5 1212. 2381. 2827. 2912.
                        3757.
    Powers (db):        -13.5 -18.8 -21.0 -22.8 -17.9 -20.1 -21.9 -22.9 -27.8 -23.6 -24.8 -30.0
                        -27.7
    AODs (Deg):         0    89   89   89   13   13   13   35   -64  -33  53   -132
                        77
    AOAs (Deg):         -180 89   89   89   163  163  163  -137 74   128  -120 -9
                        -84
    ZODs (Deg):         98   86   86   86   98   98   98   98   88   91   104  80
                        86
    ZOAs (Deg):         82   87   87   87   79   79   79   78   74   78   87   71
                        73
[5]:
# Apply the channel in time domain:
maxDelay = channel.getMaxDelay()
paddedTxWaveform = txWaveform.pad(maxDelay)              # Pad the waveform with zeros
rxWaveform = channel.applyToSignal(paddedTxWaveform)     # Apply the channel to the waveform

syncedWaveform = rxWaveform.sync(channel.chanOffset)                 # Synchronization
rxGridTd = syncedWaveform.ofdmDemodulate(bwp, f0=carrierFreq)

# Apply the channel in frequency domain and compare
chanMat = channel.getChannelMatrix()
rxGridFd = txGrid.applyChannel(chanMat)
print(f"NMSE: {getNmse(rxGridFd.grid,rxGridTd.grid)}")

NMSE: 7.131743705718165e-05
[6]:
# Apply the channel to 100 slots of input signal
chanMats = []
for s in range(100):
    # Go to next slot and do it again:
    channel.goNext()

    txGrid = bwp.createGrid(numPlanes=8)

    stats = txGrid.getStats()
    numRandomBits = stats['UNASSIGNED']*modem.qm

    bits = random.bits(numRandomBits)
    symbols = modem.modulate(bits)

    indexes = txGrid.getReIndexes("UNASSIGNED")
    txGrid[indexes] = symbols

    txWaveform = txGrid.ofdmModulate(carrierFreq)

    # Apply the channel in time domain:
    maxDelay = channel.getMaxDelay()
    paddedTxWaveform = txWaveform.pad(maxDelay)              # Pad the waveform with zeros
    rxWaveform = channel.applyToSignal(paddedTxWaveform)     # Apply the channel to the waveform

    syncedWaveform = rxWaveform.sync(channel.chanOffset)                 # Synchronization
    rxGridTd = syncedWaveform.ofdmDemodulate(bwp, f0=carrierFreq)

    # Apply the channel in frequency domain and compare
    chanMat = channel.getChannelMatrix()
    rxGridFd = txGrid.applyChannel(chanMat)
    assert getNmse(rxGridFd.grid,rxGridTd.grid)<1e-3
    chanMats += [chanMat]

[7]:
gs = np.concatenate([chanMat[:,0,0,0] for chanMat in chanMats])
plt.plot(np.abs(gs))
plt.title("The channel gains for the first pair of RX/TX antenna \n"+
          "and first subcarrier during 100 slots")
plt.grid()
../../../../_images/source_Playground_Notebooks_Channels_cdlTiming_7_0.png
[ ]: