{ "cells": [ { "cell_type": "markdown", "id": "b7e0a807", "metadata": {}, "source": [ "# Channel Matrix\n", "This notebook shows different ways of applying a channel to a signal in time and frequency domains." ] }, { "cell_type": "code", "execution_count": 1, "id": "b5013617", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy.io\n", "import time\n", "\n", "from neoradium import Carrier, Modem, CdlChannel, AntennaPanel, Grid, random\n", "from neoradium.utils import getNmse" ] }, { "cell_type": "code", "execution_count": 2, "id": "d0952472", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Shape of Resource Grid: (8, 14, 300)\n", "Shape of Waveform: (8, 30720)\n" ] } ], "source": [ "# Create a valid random grid\n", "random.setSeed(123) # Make results reproducible\n", "carrier = Carrier(startRb=0, numRbs=25, spacing=15) # Carrier 25 Resource Blocks, 15KHz subcarrier spacing\n", "bwp = carrier.curBwp # The only bandwidth part in the carrier\n", "txGrid = bwp.createGrid(numPlanes=8) # Create an empty resource grid\n", "\n", "stats = txGrid.getStats() # Get statistics about the grid\n", "modem = Modem(\"16QAM\") # Using 16QAM modulation\n", "numRandomBits = stats['UNASSIGNED']*modem.qm # Total number of bits available in the resource grid\n", "\n", "bits = random.bits(numRandomBits) # Create random bits\n", "symbols = modem.modulate(bits) # Modulate the bits to get symbols\n", "\n", "indexes = txGrid.getReIndexes(\"UNASSIGNED\") # Indexes of the \"UNASSIGNED\" resources\n", "txGrid[indexes] = symbols # Put symbols in the resource grid\n", "\n", "txWaveform = txGrid.ofdmModulate() # OFDM-Modulate the resource grid to get a waveform\n", "\n", "print(\"Shape of Resource Grid:\",txGrid.shape)\n", "print(\"Shape of Waveform: \",txWaveform.shape)\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "555f4b1b", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "CDL-C Channel Properties:\n", " carrierFreq: 4 GHz\n", " normalizeGains: True\n", " normalizeOutput: True\n", " txDir: Downlink\n", " filterLen: 16 samples\n", " delayQuantSize: 64\n", " stopBandAtten: 80 db\n", " dopplerShift: 5 Hz\n", " coherenceTime: 84.628 milliseconds\n", " delaySpread: 300 ns\n", " ueDirAZ: 0.0°, 90.0°\n", " Cross Pol. Power: 7 db\n", " angleSpreads: 2° 15° 3° 7°\n", " TX Antenna:\n", " Total Elements: 8\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 2 rows x 4 columns\n", " polarization: |\n", " Orientation (𝛼,𝛃,𝛄): 0° 0° 0°\n", " RX Antenna:\n", " Total Elements: 2\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 1 rows x 2 columns\n", " polarization: |\n", " hasLOS: False\n", " NLOS Paths (24):\n", " Delays (ns): 0.000 62.97 66.57 69.87 65.28 190.9 193.4 196.8 197.5 238.0 246.3 280.0\n", " 368.5 392.4 651.1 813.1 1277. 1380. 1647. 1682. 1891. 1991. 2112. 2595.\n", " Powers (db): -4.40 -1.20 -3.50 -5.20 -2.50 0.000 -2.20 -3.90 -7.40 -7.10 -10.7 -11.1\n", " -5.10 -6.80 -8.70 -13.2 -13.9 -13.9 -15.8 -17.1 -16.0 -15.7 -21.6 -22.8\n", " AODs (Deg): -47 -23 -23 -23 -41 0 0 0 73 -64 80 -97 \n", " -55 -64 -78 103 99 89 -102 92 93 107 119 -124\n", " AOAs (Deg): -101 120 120 120 -128 170 170 170 55 66 -48 47 \n", " 68 -69 82 31 -16 4 -14 10 6 1 -22 34 \n", " ZODs (Deg): 97 99 99 99 101 99 99 99 105 95 106 94 \n", " 104 104 93 104 95 93 92 107 93 93 105 108 \n", " ZOAs (Deg): 88 72 72 72 70 75 75 75 67 64 71 60 \n", " 91 60 61 101 62 67 53 62 52 62 58 57 \n", "\n" ] } ], "source": [ "# Create a CDL-C channel model with 300ns delay spread, 4GHz carrier frequency, and 5Hz doppler shift\n", "channel = CdlChannel(bwp, 'C', delaySpread=300, carrierFreq=4e9, dopplerShift=5,\n", " txAntenna = AntennaPanel([2,4], polarization=\"|\"), # 8 TX antenna\n", " rxAntenna = AntennaPanel([1,2], polarization=\"|\")) # 2 RX antenna\n", "print(channel)" ] }, { "cell_type": "code", "execution_count": 4, "id": "5b938fa2", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time to apply channel in Freq. Domain: 0.004931926727294922\n" ] } ], "source": [ "# Apply the channel in Frequency Domain:\n", "t0 =time.time()\n", "channelMatrix = channel.getChannelMatrix()\n", "rxGridF = txGrid.applyChannel(channelMatrix)\n", "t1 =time.time()\n", "print(\"Time to apply channel in Freq. Domain:\", t1-t0)" ] }, { "cell_type": "code", "execution_count": 5, "id": "5beb0236", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time to apply channel in Time Domain: 0.28674793243408203\n", "NMSE between the rxGrid in Time and Freq. domains: 6.7222404693767e-10\n" ] } ], "source": [ "# Applying the channel in time domain and demodulate to get a received resource grid (rxGrid)\n", "t0 =time.time()\n", "maxDelay = channel.getMaxDelay() # Calculate the channel's max delay\n", "paddedTxWaveform = txWaveform.pad(maxDelay) # Pad the waveform with zeros\n", "rxWaveform = channel.applyToSignal(paddedTxWaveform) # Apply the channel to the waveform\n", "offset = channel.getTimingOffset() # Get the timing offset for synchronization\n", "syncedWaveform = rxWaveform.sync(offset) # Synchronization\n", "rxGridT = Grid.ofdmDemodulate(bwp, syncedWaveform) # OFDM-demodulation\n", "t1 =time.time()\n", "print(\"Time to apply channel in Time Domain:\", t1-t0)\n", "print(\"NMSE between the rxGrid in Time and Freq. domains: \", getNmse(rxGridT.grid,rxGridF.grid)) \n" ] }, { "cell_type": "code", "execution_count": null, "id": "2c6e0954", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.6" } }, "nbformat": 4, "nbformat_minor": 5 }