{ "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 CdlChannel, AntennaPanel, random\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", " delaySpread: 10 ns\n", " dopplerShift: 55.59401586635868 Hz\n", " carrierFreq: 4000000000.0 Hz\n", " normalizeGains: True\n", " normalizeOutput: True\n", " txDir: Downlink\n", " timing method: Matlab\n", " coherenceTime: 0.007611 (Sec.)\n", " ueDirAZ: 0°, 90°\n", " Angle Scaling:\n", " Means: 130° 70° 80° 110°\n", " RMS Spreads: 5° 11° 3° 3°\n", " pathDelays (ns): 0.0000 0.0000 0.3500 6.1200 13.630 14.050 18.040 25.960 17.750 40.420\n", " 79.370 94.240 97.080 125.25\n", " pathPowers (db): -0.200 -13.50 -18.80 -21.00 -22.80 -17.90 -20.10 -21.90 -22.90 -27.80\n", " -23.60 -24.80 -30.00 -27.70\n", " AODs (Degree): 0 0 89 89 89 13 13 13 35 -64 -33 53 -132 77\n", " AOAs (Degree): -180 -180 89 89 89 163 163 163 -137 74 128 -120 -9 -84\n", " ZODs (Degree): 98 98 86 86 86 98 98 98 98 88 91 104 80 86\n", " ZOAs (Degree): 82 82 87 87 87 79 79 79 78 74 78 87 71 73\n", " hasLOS: True\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", " taper: 1.0\n", " TX Antenna 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", " taper: 1.0\n", " Channel Filter:\n", " filterDelay (samples): 7\n", " numTxAntenna: 8\n", " numPaths: 14\n", " pathDelays (ns): 0.0000 0.0000 0.3500 6.1200 13.630 14.050 18.040 25.960 17.750 40.420\n", " 79.370 94.240 97.080 125.25\n", " filterLen: 16\n", " numInterpol: 50\n", " normalize: True\n", " stopBandAtten: 70.151\n", "\n" ] } ], "source": [ "cdlModel = 'D'\n", "seed = 123\n", "phiInit, coupling = CdlChannel.getMatlabRandomInit(cdlModel, seed)\n", "\n", "stopBandAttenuation = 70\n", "# Our calculation of beta is different from Matlab (See the ChannelFilter.getMultiRateFIR method)\n", "# The following code compensates for the difference:\n", "stopBandAttenuation += 8.861-8.71 # We use \"8.861\"; Matlab uses \"8.71\". See Note 2 above.\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('D', 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", " angleScaling = ([130,70,80,110], [5,11,3,3]), # Angle Scaling\n", " stopBandAtten = stopBandAttenuation,\n", " timing = 'Matlab') # Use Matlabmatlab timing only when comparing results with Matlab\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", " [[-0.6478-0.1199j -0.2126+1.1219j]\n", " [ 0.2027-0.3657j -0.235 +0.5164j]\n", " [ 0.2795+0.0952j -1.5704-1.3598j]\n", " [ 1.0207+0.3367j 0.2162-1.0988j]]\n", "RX Waveform Data:\n", " [-0.0091+0.0549j 0.0093-0.0458j 0.0008+0.0113j 0.025 -0.0101j]\n", "Maximum Difference: 1.5649542371081303e-10\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 Matlab-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", "diffAbs = np.abs(rxWaveformMatlab.T-rxWaveform.waveform)\n", "print(\"Maximum Difference:\", diffAbs.max()) # Maximum difference between NeoRadium and Matlab results" ] }, { "cell_type": "code", "execution_count": null, "id": "87e3f186", "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 }