{ "cells": [ { "cell_type": "markdown", "id": "2bd95eba", "metadata": {}, "source": [ "# Comparing the CDL Channel results with Matlab\n", "Compare the results of this notebook with the Matlab file ``CDLTest.mlx`` in the ``MatlabFiles`` directory.\n", "\n", "The \".mat\" files in the ``MatlabFiles`` directory were created by Matlab running the ``CDLTest.mlx`` file. If you want to recreate these files, follow the instructions in the Matlab file. [Here](MatlabFiles/CDLTest.html) is the execution results of this code in Matlab." ] }, { "cell_type": "code", "execution_count": 1, "id": "92baf17d", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy.io\n", "\n", "from neoradium import Carrier, CdlChannel, AntennaPanel, random, Waveform\n", "from neoradium.utils import getNmse\n", "\n", "matlabFilesPath = \"./MatlabFiles\"" ] }, { "cell_type": "markdown", "id": "8770aa60", "metadata": {}, "source": [ "## CDL Channel Model\n", "Now we want to create a CDL Channel object (``CdlChannel``) and apply it to the time-domain waveform. \n", "\n", "**Note 1:**\n", "Since CDL is a statistical model, there is always a randomness with the way the phases are initialized and the way rays are coupled. The ``getMatlabRandomInit`` helper function can be used to create the same random initial phases and ray couplings that are generated by the Matlab code.\n", "\n", "**Note 2:**\n", "The NeoRadium's implementation of FIR filters used by the CDL channel is slightly different from Matlab. To compensate for this difference we need to modify the ``stopBandAttenuation`` parameter. See the documentation of ``ChannelFilter`` class for more information." ] }, { "cell_type": "code", "execution_count": 2, "id": "53eddcf0", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "CDL-D Channel Properties:\n", " carrierFreq: 4 GHz\n", " normalizeGains: True\n", " normalizeOutput: True\n", " txDir: Downlink\n", " filterLen: 16 samples\n", " delayQuantSize: 64\n", " stopBandAtten: 70 dB\n", " dopplerShift: 55.59401586635868 Hz\n", " coherenceTime: 7.611 milliseconds\n", " delaySpread: 10 ns\n", " ueDirAZ: 0.0°, 90.0°\n", " Angle Scaling:\n", " Means: 130° 70° 80° 110°\n", " RMS Spreads: 5° 11° 3° 3°\n", " Cross Pol. Power: 11 dB\n", " angleSpreads: 5° 8° 3° 3°\n", " TX Antenna:\n", " Total Elements: 8\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 2 rows x 2 columns\n", " polarization: x\n", " Orientation (𝛼,𝛃,𝛄): 10° 20° 30°\n", " RX Antenna:\n", " Total Elements: 2\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 1 rows x 1 columns\n", " polarization: +\n", " Orientation (𝛼,𝛃,𝛄): 180° 0° 0°\n", " hasLOS: True\n", " LOS Path:\n", " Delay (ns): 0.00000\n", " Power (dB): -0.20000\n", " AOD (Deg): 0\n", " AOA (Deg): -3\n", " ZOD (Deg): 1\n", " ZOA (Deg): 1\n", " NLOS Paths (13):\n", " Delays (ns): 0.000 0.350 6.120 13.63 14.05 18.04 25.96 17.75 40.42 79.37 94.24 97.08\n", " 125.2\n", " 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\n", " -27.7\n", " AODs (Deg): 0 89 89 89 13 13 13 35 -64 -33 53 -132\n", " 77 \n", " AOAs (Deg): -180 89 89 89 163 163 163 -137 74 128 -120 -9 \n", " -84 \n", " ZODs (Deg): 98 86 86 86 98 98 98 98 88 91 104 80 \n", " 86 \n", " ZOAs (Deg): 82 87 87 87 79 79 79 78 74 78 87 71 \n", " 73 \n", "\n" ] } ], "source": [ "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", "\n", "cdlModel = 'D'\n", "seed = 123\n", "phiInit, coupling = CdlChannel.getMatlabRandomInit(cdlModel, seed) # Match Matlab random values\n", "\n", "speedKmh = 15 # speed of UE device: 15 km/h\n", "speed = speedKmh*1000/3600 # m/s\n", "c = 299792458 # Speed of light\n", "fc = 4e9 # 4 GHz\n", "d = speed*fc/c # Doppler Shift (Hz)\n", "\n", "# Create the channel model\n", "channel = CdlChannel(bwp, cdlModel, delaySpread=10, carrierFreq=fc, dopplerShift=d,\n", " initialPhases = phiInit, rayCoupling = coupling,\n", " txAntenna = AntennaPanel([2,2], polarization=\"x\", matlabOrder=True),\n", " rxAntenna = AntennaPanel([1,1], polarization=\"+\", matlabOrder=True),\n", " txOrientation = [10, 20, 30],\n", " rxOrientation = [180, 0, 0], # The default is [180, 0, 0]\n", " angleScaling = ([130,70,80,110], [5,11,3,3]), # Angle Scaling\n", " stopBandAtten = 70,\n", " )\n", "channel.print()" ] }, { "cell_type": "markdown", "id": "3027539c", "metadata": {}, "source": [ "## Applying the channel to a random waveform\n", "Now we create a random waveform 1 subframe long (1 ms) and apply our CDL Channel to the waveform. To compare the results with Matlab, we read the waveform from a file created by the Matlab program ``CDLTest.mlx``.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "2a25c0ad", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "TX Waveform Data:\n", " [[-1.4866-0.7976j 2.1968-0.0359j]\n", " [ 0.0656+0.775j 0.4241+1.7407j]\n", " [ 0.0862+0.9547j 0.4668+1.7866j]\n", " [-0.8242+0.6706j 0.2277+1.1032j]]\n", "RX Waveform Data:\n", " [ 0.0126-0.0086j 0.0055+0.0029j -0.0135+0.0006j -0.0057+0.0084j]\n", "NMSE: 5.4597016465344895e-05\n" ] } ], "source": [ "# Create a random signal for 1 subframe (1 ms)\n", "t = 0.001 # 1 subframe = 1 ms\n", "numInputSamples = int(channel.sampleRate * t)\n", "nr, nt = channel.nrNt # Get the number of antanna from the channel\n", "\n", "# Load the \"txWaveform\" generated by the Matlab code\n", "txWaveform = scipy.io.loadmat(matlabFilesPath+'/txWaveform.mat')['txWaveform'].T\n", "assert txWaveform.shape==(nt, numInputSamples)\n", "\n", "# Check the following numbers with the Matlabmatlab-generated numbers to make sure we are using\n", "# the same input signal:\n", "print(\"TX Waveform Data:\\n\", np.round(txWaveform[2:4,200:204].T,4)) # Matlab: txWaveform(201:204,3:4)\n", "\n", "# Use the following line instead of above line to create a random signal (The result will be \n", "# different from Matlab)\n", "# txWaveform = np.random.normal(size=(numSamples, nt)) + 1j*np.random.normal(size=(numSamples, nt))\n", "\n", "# Now apply the channel to the waveform\n", "rxWaveform = channel.applyToSignal(txWaveform) \n", "print(\"RX Waveform Data:\\n\", np.round(rxWaveform[1,200:204].T,4)) # Matlab: rxWaveform(201:204,2)\n", "\n", "# Load Matlab results and compare with the above results\n", "rxWaveformMatlab = scipy.io.loadmat(matlabFilesPath+'/rxWaveform.mat')['rxWaveform']\n", "assert rxWaveformMatlab.shape==(numInputSamples, nr)\n", "print(\"NMSE:\", getNmse(rxWaveformMatlab.T,rxWaveform.waveform)) # NMSE between NeoRadium and Matlab results" ] }, { "cell_type": "code", "execution_count": null, "id": "356e3772-aa2d-4e6b-b644-397d5a13633b", "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 }