{ "cells": [ { "cell_type": "markdown", "id": "9739da1c", "metadata": {}, "source": [ "# CSI-RS Configuration for Beam Management\n", "\n", "This notebook demonstrates how **NeoRadium** can be used to configure and visualize CSI-RS resources for 5G NR beam-management procedures.\n", "\n", "The example uses a multi-antenna CDL channel and the `CsiRsConfig.beamformingConfig` utility to create a set of CSI-RS resources representative of a practical beamforming workflow. These resources are organized into multiple CSI-RS resource sets that support different stages of beam management:\n", "\n", "- **Periodic beam sweeping**, used to identify candidate transmission beams.\n", "- **Aperiodic beam probing**, used to refine beam selection around the best sweeping beam.\n", "- **Semi-persistent CSI feedback measurements**, used for PMI, RI, and CQI reporting.\n", "\n", "The notebook first creates and examines the CSI-RS configuration generated by `beamformingConfig`. It then visualizes how the different CSI-RS resource sets are scheduled over time, illustrating the interaction between periodic, aperiodic, and semi-persistent CSI-RS transmissions.\n", "\n", "By following the examples and animations, you can see how CSI-RS resources are distributed across slots and how **NeoRadium** models CSI-RS signaling for beam sweeping, beam refinement, and CSI feedback in a beam-management scenario." ] }, { "cell_type": "code", "execution_count": 1, "id": "d49a98b8", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy.io\n", "import matplotlib.animation as animation\n", "import matplotlib.pyplot as plt\n", "from IPython.display import HTML, Markdown, display\n", "\n", "from neoradium import BandwidthPart, CsiRsConfig, CdlChannel, AntennaPanel" ] }, { "cell_type": "code", "execution_count": 2, "id": "b9033179", "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: 30 ns\n", " ueDirAZ: 0°, 90°\n", " xPolPower: 7.00 dB\n", " angleSpreads: 2° 15° 3° 7°\n", " TX Antenna:\n", " Total Elements: 8\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 1 rows x 4 columns\n", " polarization: x\n", " RX Antenna:\n", " Total Elements: 4\n", " spacing: 0.5𝜆, 0.5𝜆\n", " shape: 1 rows x 2 columns\n", " polarization: x\n", " Orientation (𝛼,𝛃,𝛄): 180° 0° 0°\n", " hasLOS: False\n", " NLOS Paths (24):\n", " Delays (ns): 0.000 6.297 6.657 6.987 6.528 19.09 19.34 19.68 19.75 23.80 24.63 28.00\n", " 36.85 39.24 65.11 81.31 127.7 138.0 164.7 168.2 189.1 199.1 211.2 259.5\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 120 -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 'BandwidthPart' and a CDL-C channel object:\n", "bwp = BandwidthPart(numRbs=52, spacing=15)\n", "\n", "channel = CdlChannel(bwp, profile='C', delaySpread=30, carrierFreq=4e9, dopplerShift=5,\n", " txAntenna=AntennaPanel([1,4], polarization='x', beamWidth=[65,65]),\n", " rxAntenna=AntennaPanel([1,2], polarization='x', beamWidth=[65,360]),\n", " rxOrientation = [180,0,0])\n", "print(channel)\n" ] }, { "cell_type": "markdown", "id": "ff4f0f7b-ffc1-464b-9963-840891743559", "metadata": {}, "source": [ "## Using ``beamformingConfig``\n", "The utility class method ``beamformingConfig`` creates a ``CsiRsConfig`` object suitable for typical FR1 beamforming experiments. It creates three CSI-RS resource sets:\n", "\n", "- A **periodic sweeping set** of 1-port resources used to obtain a coarse CRI.\n", "- An **aperiodic probing set** of 1-port resources used to refine the CRI around the best sweep beam.\n", "- A **semi-persistent multi-port set** used to measure PMI, RI, and CQI.\n", "\n", "Sweeping and probing CSI-RS are transmitted on OFDM symbol 4, while the PMI CSI-RS is sent on OFDM symbol 6. Up to four 1-port CSI-RS resources are packed into a slot.\n", "\n", "**Notes:**\n", "1) This method uses a simple static beam sweeping/probing approach. In practice, adaptive beam sweeping can be used by incorporating the previously selected probing beam into the subsequent sweeping set and centering future sweeps around it. This approach resembles a wider beam probing around the current best beam, including a few wider beams to detect beam drift and prevent getting stuck in a local maximum.\n", "\n", "2) The [beamformingReports](https://ail-wireless.pages.interdigital.com/neoradium/source/API/CsiReport.html#neoradium.csireport.CsiReportMan.beamformingReports) class method may be used to create a ``CsiReportMan`` object containing CSI report information corresponding to the CSI-RS resources created by ``beamformingConfig``.\n", "\n", "For more details, refer to the documentation of [beamformingConfig](https://ail-wireless.pages.interdigital.com/neoradium/source/API/RefSig.html#neoradium.csirs.CsiRsConfig.beamformingConfig)." ] }, { "cell_type": "code", "execution_count": 3, "id": "7d8dbefc-0052-4c4e-8b4f-ffe807c9f1d1", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "CSI-RS Configuration: (3 Resource Sets)\n", " CSI-RS Resource Set 1:(8 NZP resources)\n", " Resource Set ID: 1\n", " Resource Type: periodic\n", " Resource Blocks: 52 RBs starting at 0\n", " Slot Period: 20\n", " Num CSI-RS: 8\n", " CSI-RS 1:\n", " resourceId: 1\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 2\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 0\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 2:\n", " resourceId: 2\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 3\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 0\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 3:\n", " resourceId: 3\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 4\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 0\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 4:\n", " resourceId: 4\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 5\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 0\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 5:\n", " resourceId: 5\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 2\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 1\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 6:\n", " resourceId: 6\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 3\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 1\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 7:\n", " resourceId: 7\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 4\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 1\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 8:\n", " resourceId: 8\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 5\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Slot Offset: 1\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS Resource Set 2:(4 NZP resources)\n", " Resource Set ID: 2\n", " Resource Type: aperiodic\n", " Resource Blocks: 52 RBs starting at 0\n", " active: False\n", " Num CSI-RS: 4\n", " CSI-RS 9:\n", " resourceId: 9\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 2\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 10:\n", " resourceId: 10\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 3\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 11:\n", " resourceId: 11\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 4\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS 12:\n", " resourceId: 12\n", " numPorts: 1\n", " cdmSize: 1 (noCDM)\n", " density: 1\n", " RE Indices: 5\n", " Symbol Indices: 4\n", " Table Row: 2\n", " Power: 0 dB\n", " scramblingID: 0\n", " CSI-RS Resource Set 3:(1 NZP resources)\n", " Resource Set ID: 3\n", " Resource Type: semiPersistent\n", " Resource Blocks: 52 RBs starting at 0\n", " Slot Period: 10\n", " active: False\n", " Num CSI-RS: 1\n", " CSI-RS 13:\n", " resourceId: 13\n", " numPorts: 8\n", " cdmSize: 2 (fd-CDM2)\n", " density: 1\n", " RE Indices: 6 8\n", " Symbol Indices: 5\n", " Table Row: 7\n", " Slot Offset: 0\n", " Power: 0 dB\n", " scramblingID: 0\n", "\n" ] } ], "source": [ "csiRsConfig = CsiRsConfig.beamformingConfig(bwp, channel.txAntenna.numPorts, sweepsPerSlot=4)\n", "print(csiRsConfig)" ] }, { "cell_type": "markdown", "id": "a7181f87-30d3-425f-9e40-6e7c639c63e0", "metadata": {}, "source": [ "## Timing Aspects of CSI-RS Resources\n", "\n", "The following code illustrates CSI-RS resources on a grid map for the first 40 slots of communication. As the animation runs, observe the following:\n", "\n", "* CSI-RS for sweeping beams is transmitted every 20 slots. The first four beams are sent at slots 0 and 20, while the next four beams are transmitted on slots 1 and 21.\n", "* CSI-RS for beam probing is aperiodic. In the following code, they are triggered on slots ``[5, 15, 27, 32]`` (hard-coded for this example).\n", "* The CSI-RS resource set for CSI feedback (e.g., PMI, RI, CQI) is semi-persistent. It is activated at slot 18. With a period of 10, it appears on slots 20 and 30." ] }, { "cell_type": "code", "execution_count": 4, "id": "d4cc14b7-8fa2-4804-8250-a37872a0198e", "metadata": {}, "outputs": [ { "data": { "text/markdown": [ "![demo](CsiRsBeams.gif)" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "csiRsConfig[2].active=False\n", "txGrid = bwp.createGrid(csiRsConfig.numPorts)\n", "csiRsConfig.populateGrid(txGrid)\n", "stats = txGrid.getStats()\n", "rsIdStrs = [ key.split(\"(\")[1][:-1] for key,val in stats.items() if \"CSIRS_NZP\" in key ]\n", "\n", "axes = txGrid.drawMap(rbRange=(0,1), \n", " title=f\"Grid map for slot {0} with CSI-RS Resource IDs: {\",\".join(rsIdStrs)}\")\n", "\n", "# Callback function to update the grid map\n", "def updateMap(frame):\n", " axes[0].clear()\n", " bwp.slotNo = frame\n", "\n", " if bwp.slotNo in [5, 15, 27, 32]: csiRsConfig[1].trigger()\n", " if bwp.slotNo == 18: csiRsConfig[2].active=True\n", " \n", " txGrid = bwp.createGrid(csiRsConfig.numPorts)\n", " csiRsConfig.populateGrid(txGrid)\n", " stats = txGrid.getStats()\n", " rsIdStrs = [ key.split(\"(\")[1][:-1] for key,val in stats.items() if \"CSIRS_NZP\" in key ]\n", " title = f\"Grid map for slot {frame} ({'No CSI-RS resources' if len(rsIdStrs)==0 else ('CSI-RS Resource IDs: '+\",\".join(rsIdStrs))})\"\n", " txGrid.drawMap(rbRange=(0,1), title=title, axes=axes)\n", " return ()\n", "\n", "# Create the animation\n", "fig = axes[0].get_figure()\n", "anim = animation.FuncAnimation(fig, updateMap, frames=40, interval=1000, blit=False, repeat=True)\n", "plt.close(fig) # Prevent duplicate static plots\n", "\n", "anim.save(\"CsiRsBeams.gif\", writer=animation.PillowWriter(1)) # Show one frames per second\n", "display(Markdown(\"![demo](CsiRsBeams.gif)\"))\n", "\n", "# Another option is to use the following command which gives you more controls for running\n", "# the animation.\n", "# HTML(anim.to_jshtml())" ] }, { "cell_type": "code", "execution_count": null, "id": "624f44f8-d3c2-44a7-9b5f-cd3b1f36c484", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "27e7cdb0-a8ba-4657-9a85-b55919cb30b8", "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.12.10" } }, "nbformat": 4, "nbformat_minor": 5 }