{ "cells": [ { "cell_type": "markdown", "id": "c2c61148-3850-492a-876f-ac5d39cd0d8a", "metadata": {}, "source": [ "# Hierarchical Beam Management with Sweeping, Probing, and Type-I PMI Feedback\n", "\n", "This example demonstrates a hierarchical downlink beam-management and CSI-feedback procedure. The gNB first identifies a suitable transmit direction using one-port CSI-RS resources and then performs multi-layer precoding within the selected direction using a directional multi-port CSI-RS resource.\n", "\n", "The procedure consists of three stages:\n", "\n", "1. **Beam sweeping:** \n", " The gNB transmits a set of one-port CSI-RS resources using relatively coarse steering directions. The UE measures these resources and reports a **CSI-RS Resource Indicator (CRI)** identifying the preferred sweeping beam.\n", "\n", "2. **Beam probing:** \n", " Based on the sweeping result, the gNB transmits a second set of one-port CSI-RS resources using finer steering directions around the selected sweeping beam. The UE reports a second CRI identifying the preferred probing beam.\n", "\n", "3. **RI, PMI, and CQI acquisition:** \n", " The gNB transmits a multi-port CSI-RS using the steering vector associated with the selected probing beam. The UE estimates the corresponding effective MIMO channel and reports:\n", " - **Rank Indicator (RI):** the recommended number of transmission layers;\n", " - **Precoding Matrix Indicator (PMI):** the preferred Type-I single-panel codebook precoder; and\n", " - **Channel Quality Indicator (CQI):** the recommended modulation and code-rate operating point.\n", "\n", "After receiving the RI, PMI, and CQI report, the gNB configures the PDSCH and forms a composite precoder by combining:\n", "\n", "- the steering vector associated with the selected probing CRI; and\n", "- the Type-I codebook precoder indicated by the PMI.\n", "\n", "The same steering transformation used for the directional multi-port CSI-RS is therefore also applied to the PDSCH. This ensures that the effective channel used by the UE for PMI selection is consistent with the effective channel experienced by the precoded data transmission.\n", "\n", "The simulation includes:\n", "\n", "1. coarse beam sweeping using one-port CSI-RS resources;\n", "2. fine beam probing around the selected sweeping direction;\n", "3. transmission of a directional multi-port CSI-RS using the selected probing beam;\n", "4. UE-side CRI, RI, PMI, and CQI calculation;\n", "5. gNB-side PDSCH configuration based on the reported RI and CQI;\n", "6. PDSCH transmission using the combined probing-beam steering vector and PMI precoder; and\n", "7. receiver equalization, LDPC decoding, and transport-block CRC verification.\n", "\n", "The feedback generated at each stage is applied to subsequent transmissions. Consequently, the sweeping result determines the probing region, the probing result determines the steering vector used for the multi-port CSI-RS, and the resulting RI/PMI/CQI report determines the configuration and precoding of later PDSCH transmissions." ] }, { "cell_type": "code", "execution_count": 1, "id": "e8e65ac4-d9e4-40a6-8c40-f8021d0f2034", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "from neoradium import BandwidthPart, PDSCH, AntennaPanel, CdlChannel, random\n", "from neoradium import CsiRsConfig, CsiReportMan" ] }, { "cell_type": "code", "execution_count": 2, "id": "291f4cee-2f5c-470f-aab3-afcee132a6eb", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Slot 0: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 1: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 5: Received CRI (ReportID: 11), best beam (𝛳=90.00°, 𝝋=-7.11°), RSRP: 13.09 dB\n", "Slot 5: Sending probing CSI-RS (Set ID:2, 4 beams)\n", "Slot 6: Received CRI (ReportID: 12), best beam (𝛳=95.00°, 𝝋=-7.11°), RSRP: 14.07 dB\n", "Slot 10: Sending PMI resources (Set ID:3)\n", "Slot 14: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.117)\n", " WB PMI: (I1:[0, 0, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 14: Starting PDSCH (Mod:QPSK, Coderate:449/1024)\n", "Slot 14: TxBlock CRC Match: True\n", "Slot 15: TxBlock CRC Match: True\n", "Slot 16: TxBlock CRC Match: True\n", "Slot 17: TxBlock CRC Match: True\n", "Slot 18: TxBlock CRC Match: True\n", "Slot 19: TxBlock CRC Match: True\n", "Slot 20: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 20: Sending PMI resources (Set ID:3)\n", "Slot 20: TxBlock CRC Match: True\n", "Slot 21: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 21: TxBlock CRC Match: True\n", "Slot 22: TxBlock CRC Match: True\n", "Slot 23: TxBlock CRC Match: True\n", "Slot 24: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.286)\n", " WB PMI: (I1:[0, 0, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 24: TxBlock CRC Match: True\n", "Slot 25: Received CRI (ReportID: 11), best beam (𝛳=90.00°, 𝝋=-7.11°), RSRP: 13.65 dB\n", "Slot 25: Sending probing CSI-RS (Set ID:2, 4 beams)\n", "Slot 25: TxBlock CRC Match: True\n", "Slot 26: Received CRI (ReportID: 12), best beam (𝛳=95.00°, 𝝋=-7.11°), RSRP: 13.51 dB\n", "Slot 26: TxBlock CRC Match: True\n", "Slot 27: TxBlock CRC Match: True\n", "Slot 28: TxBlock CRC Match: True\n", "Slot 29: TxBlock CRC Match: True\n", "Slot 30: Sending PMI resources (Set ID:3)\n", "Slot 30: TxBlock CRC Match: True\n", "Slot 31: TxBlock CRC Match: True\n", "Slot 32: TxBlock CRC Match: True\n", "Slot 33: TxBlock CRC Match: True\n", "Slot 34: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.125)\n", " WB PMI: (I1:[0, 0, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 34: TxBlock CRC Match: True\n", "Slot 35: TxBlock CRC Match: True\n", "Slot 36: TxBlock CRC Match: True\n", "Slot 37: TxBlock CRC Match: True\n", "Slot 38: TxBlock CRC Match: True\n", "Slot 39: TxBlock CRC Match: True\n", "Slot 40: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 40: Sending PMI resources (Set ID:3)\n", "Slot 40: TxBlock CRC Match: True\n", "Slot 41: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 41: TxBlock CRC Match: True\n", "Slot 42: TxBlock CRC Match: True\n", "Slot 43: TxBlock CRC Match: True\n", "Slot 44: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.335)\n", " WB PMI: (I1:[0, 0, 0], I2:1)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 44: TxBlock CRC Match: True\n", "Slot 45: Received CRI (ReportID: 11), best beam (𝛳=90.00°, 𝝋=7.11°), RSRP: 13.53 dB\n", "Slot 45: Sending probing CSI-RS (Set ID:2, 4 beams)\n", "Slot 45: TxBlock CRC Match: True\n", "Slot 46: Received CRI (ReportID: 12), best beam (𝛳=90.00°, 𝝋=1.10°), RSRP: 14.21 dB\n", "Slot 46: TxBlock CRC Match: True\n", "Slot 47: TxBlock CRC Match: True\n", "Slot 48: TxBlock CRC Match: True\n", "Slot 49: TxBlock CRC Match: True\n", "Slot 50: Sending PMI resources (Set ID:3)\n", "Slot 50: TxBlock CRC Match: True\n", "Slot 51: TxBlock CRC Match: True\n", "Slot 52: TxBlock CRC Match: True\n", "Slot 53: TxBlock CRC Match: True\n", "Slot 54: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.460)\n", " WB PMI: (I1:[1, 0, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 6\n", " Modulation: QPSK\n", " Coderate: 602/1024\n", " CQI BLER: 4.16 %\n", "Slot 54: CQI changed -> Coderate:602/1024\n", "Slot 54: TxBlock CRC Match: True\n", "Slot 55: TxBlock CRC Match: True\n", "Slot 56: TxBlock CRC Match: True\n", "Slot 57: TxBlock CRC Match: True\n", "Slot 58: TxBlock CRC Match: True\n", "Slot 59: TxBlock CRC Match: True\n", "Slot 60: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 60: Sending PMI resources (Set ID:3)\n", "Slot 60: TxBlock CRC Match: True\n", "Slot 61: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 61: TxBlock CRC Match: True\n", "Slot 62: TxBlock CRC Match: True\n", "Slot 63: TxBlock CRC Match: True\n", "Slot 64: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.466)\n", " WB PMI: (I1:[1, 0, 0], I2:1)\n", " WB precoder shape: (16, 2)\n", " CQI: 6\n", " Modulation: QPSK\n", " Coderate: 602/1024\n", " CQI BLER: 2.00 %\n", "Slot 64: TxBlock CRC Match: True\n", "Slot 65: Received CRI (ReportID: 11), best beam (𝛳=90.00°, 𝝋=7.11°), RSRP: 14.01 dB\n", "Slot 65: Sending probing CSI-RS (Set ID:2, 4 beams)\n", "Slot 65: TxBlock CRC Match: True\n", "Slot 66: Received CRI (ReportID: 12), best beam (𝛳=90.00°, 𝝋=1.10°), RSRP: 13.65 dB\n", "Slot 66: TxBlock CRC Match: True\n", "Slot 67: TxBlock CRC Match: True\n", "Slot 68: TxBlock CRC Match: True\n", "Slot 69: TxBlock CRC Match: True\n", "Slot 70: Sending PMI resources (Set ID:3)\n", "Slot 70: TxBlock CRC Match: False\n", "Slot 71: TxBlock CRC Match: True\n", "Slot 72: TxBlock CRC Match: True\n", "Slot 73: TxBlock CRC Match: True\n", "Slot 74: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.277)\n", " WB PMI: (I1:[1, 1, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 74: CQI changed -> Coderate:449/1024\n", "Slot 74: TxBlock CRC Match: True\n", "Slot 75: TxBlock CRC Match: True\n", "Slot 76: TxBlock CRC Match: True\n", "Slot 77: TxBlock CRC Match: True\n", "Slot 78: TxBlock CRC Match: True\n", "Slot 79: TxBlock CRC Match: True\n", "Slot 80: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 80: Sending PMI resources (Set ID:3)\n", "Slot 80: TxBlock CRC Match: True\n", "Slot 81: Sending sweeping CSI-RS (Set ID:1, 4 beams)\n", "Slot 81: TxBlock CRC Match: True\n", "Slot 82: TxBlock CRC Match: True\n", "Slot 83: TxBlock CRC Match: True\n", "Slot 84: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.333)\n", " WB PMI: (I1:[0, 0, 0], I2:0)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 84: TxBlock CRC Match: True\n", "Slot 85: Received CRI (ReportID: 11), best beam (𝛳=90.00°, 𝝋=-7.11°), RSRP: 13.68 dB\n", "Slot 85: Sending probing CSI-RS (Set ID:2, 4 beams)\n", "Slot 85: TxBlock CRC Match: True\n", "Slot 86: Received CRI (ReportID: 12), best beam (𝛳=90.00°, 𝝋=-1.10°), RSRP: 14.54 dB\n", "Slot 86: TxBlock CRC Match: True\n", "Slot 87: TxBlock CRC Match: True\n", "Slot 88: TxBlock CRC Match: True\n", "Slot 89: TxBlock CRC Match: True\n", "Slot 90: Sending PMI resources (Set ID:3)\n", "Slot 90: TxBlock CRC Match: True\n", "Slot 91: TxBlock CRC Match: True\n", "Slot 92: TxBlock CRC Match: True\n", "Slot 93: TxBlock CRC Match: True\n", "Slot 94: Received RI/PMI/CQI (ReportID: 13)\n", " RI: 2 (Score:3.186)\n", " WB PMI: (I1:[1, 0, 0], I2:1)\n", " WB precoder shape: (16, 2)\n", " CQI: 5\n", " Modulation: QPSK\n", " Coderate: 449/1024\n", " CQI BLER: 0.12 %\n", "Slot 94: TxBlock CRC Match: True\n", "Slot 95: TxBlock CRC Match: True\n", "Slot 96: TxBlock CRC Match: True\n", "Slot 97: TxBlock CRC Match: True\n", "Slot 98: TxBlock CRC Match: True\n", "Slot 99: TxBlock CRC Match: True\n", "1 of 100 slots failed.\n" ] } ], "source": [ "numSlots = 100 # Number of slots in the communication loop\n", "snrDb = -10 # SNR in dB\n", "random.setSeed(1234) # Make results reproducible\n", "prgSize = 0 # Set to 0 for wideband, 2 or 4 for subband precoding\n", "\n", "# Create a bandwidth part with 24 resource blocks and 15 kHz subcarrier spacing\n", "bwp = BandwidthPart(numRbs=24, spacing=15) \n", "\n", "# Create a CDL channel model\n", "channel = CdlChannel(bwp, profile='C', delaySpread=30, carrierFreq=4e9, dopplerShift=5,\n", " txAntenna=AntennaPanel([2,4], polarization='x'), # 16 TX antennas\n", " rxAntenna=AntennaPanel([1,2], polarization='x'), # 4 RX antennas\n", " rxOrientation = [180,0,0])\n", "\n", "# Create a typical CSI-RS configuration\n", "csiRsConfig = CsiRsConfig.beamformingConfig(bwp, channel.txAntenna.numPorts, sweepsPerSlot=4)\n", "# csiRsConfig.print() # Uncomment to print CSI-RS configuration details\n", "\n", "# Get the CSI resource sets for beam sweeping, beam probing, and RI/PMI/CQI feedback\n", "sweepSet, probeSet, pmiSet = csiRsConfig.csiRsSetList \n", "\n", "# Create CSI reports and a CsiReportMan object for the CSI-RS configuration above\n", "# Note that we are enabling subband precoders by setting 'prgSize' to 4, and limitting the rank\n", "# to 1 by setting 'allowedRanks' to [1].\n", "csiReportMan = CsiReportMan.beamformingReports(csiRsConfig, channel.txAntenna, \n", " prgSize=prgSize, allowedRanks=[1,2])\n", "# csiReportMan.print() # Uncomment to print CSI report configuration details\n", "\n", "# Get the CSI report objects for beam sweeping, beam probing, and RI/PMI/CQI feedback\n", "sweepRep, probeRep, pmiRep = csiReportMan.csiReports\n", "\n", "numPhi = len(sweepSet) # Number of beams to sweep horizontally\n", "numTheta = 1 # Number of beams to sweep vertically (Restrict sweeping to azimuth)\n", "# Get a set of beam angles and precoders (weight vectors) for each beam\n", "sweepWs, sweepBeams = channel.txAntenna.getSweepingBeams(numTheta, numPhi)\n", "\n", "# Initialize beam sweeping parameters\n", "probeWs, probeBeams = None, None # Probing steering vecors and angles\n", "sweepCri, probeCri = None, None # The CRI feedback for sweeping and probing \n", "\n", "# The PDSCH object is created once we have the first RI/PMI/CQI feedback and \n", "# recreated later if CQI changes.\n", "pdsch = None\n", "precoder = None\n", "pmiW = None # The steering vector used by the most recent RI/PMI/CQI CSI-RS resources\n", "pdschW = None # The steering vector used by PDSCH (consistent with precoder from PMI)\n", "failedSlots = 0\n", "for slotNo in range(numSlots):\n", " channelMatrix = channel.getChannelMatrix()\n", "\n", " # Process CSI feedback:\n", " csiReportInfo = csiReportMan.getFeedback() # Get all available CSI reports from CsiReport objects\n", " for reportId, csiFeedback in csiReportInfo.items(): # Get the CSI feedback for each report\n", " if reportId == sweepRep.reportId: # sweeping report\n", " sweepCri = csiFeedback.cri.cri # CSI-RS resource ID of the best beam\n", " print(f\"Slot {slotNo}: Received CRI (ReportID: {reportId}), best beam (𝛳={sweepBeams[0][sweepCri-1]:.2f}°, \"\n", " f\"𝝋={sweepBeams[1][sweepCri-1]:.2f}°), RSRP: {csiFeedback.cri.rsrp:.2f} dB\")\n", " probeSet.trigger() # Trigger Probing CSI-RS resource set\n", " probeRep.trigger() # Trigger Probing report\n", " \n", " elif reportId == probeRep.reportId: # probing report\n", " probeCri = csiFeedback.cri.cri # CSI-RS resource ID of the best beam\n", " beamIdx = probeCri - len(sweepSet) - 1 # Index of the best beam\n", " print(f\"Slot {slotNo}: Received CRI (ReportID: {reportId}), best beam (𝛳={probeBeams[0][beamIdx]:.2f}°, \"\n", " f\"𝝋={probeBeams[1][beamIdx]:.2f}°), RSRP: {csiFeedback.cri.rsrp:.2f} dB\")\n", " pmiSet.active = True # Activate RI/PMI/CQI CSI-RS\n", " pmiRep.active = True # Activate RI/PMI/CQI measurements\n", " \n", " elif reportId == pmiRep.reportId: # RI/PMI/CQI report\n", " print(f\"Slot {slotNo}: Received RI/PMI/CQI (ReportID: {reportId})\")\n", " print(f\" RI: {csiFeedback.ri.ri} (Score:{csiFeedback.ri.score:.3f})\")\n", " print(f\" WB PMI: {csiFeedback.pmi.wbPMI}\")\n", " print(f\" WB precoder shape: {csiFeedback.pmi.wbW.shape}\")\n", " if csiFeedback.pmi.sbWs is not None:\n", " print(f\" {len(csiFeedback.pmi.sbWs)} SB precoders: \")\n", " for i, (rbIdx, w) in enumerate(csiFeedback.pmi.sbWs): \n", " print(f\" RBs: {str(rbIdx):<20} precoder shape: {str(w.shape):<10} PMI: {csiFeedback.pmi.sbPMIs[i]}\") \n", " if \"cqi\" in pmiRep.quantity.lower():\n", " print(f\" CQI: {csiFeedback.cqi.cqi}\")\n", " modulation, coderateX1024 = pmiRep.getModRate(csiFeedback.cqi.cqi)\n", " print(f\" Modulation: {modulation}\")\n", " print(f\" Coderate: {coderateX1024}/1024\")\n", " print(f\" CQI BLER: {csiFeedback.cqi.bler:.2f} %\")\n", "\n", " precoder = csiFeedback.pmi.wbW if csiFeedback.pmi.sbWs is None else csiFeedback.pmi.sbWs\n", " pdschW = pmiW\n", " if pdsch is None: \n", " # First RI/PMI/CQI feedback -> create PDSCH and LDPC codec objects\n", " print(f\"Slot {slotNo}: Starting PDSCH (Mod:{modulation}, \"\n", " f\"Coderate:{coderateX1024}/1024)\")\n", " pdsch = PDSCH(bwp, numLayers=csiFeedback.ri.ri, csiRsConfig=csiRsConfig, \n", " modulation=modulation, prgSize=pmiRep.prgSize)\n", " pdsch.setDMRS(additionalPos=2)\n", " ldpc = pdsch.getLdpcCodec(coderates = coderateX1024/1024)\n", " \n", " elif ( (pdsch.modems[0].modulation != modulation) or \n", " (pdsch.numLayers != csiFeedback.ri.ri) ):\n", " # Modulation or number of layers changed -> Recreate PDSCH and LDPC codec objects\n", " print(f\"Slot {slotNo}: CQI changed -> Mod:{modulation}, \"\n", " f\"Coderate:{coderateX1024}/1024\")\n", " pdsch = PDSCH(bwp, numLayers=csiFeedback.ri.ri, csiRsConfig=csiRsConfig, \n", " modulation=modulation, prgSize=pmiRep.prgSize)\n", " pdsch.setDMRS(additionalPos=2)\n", " ldpc = pdsch.getLdpcCodec(coderates = coderateX1024/1024)\n", " \n", " elif ldpc.coderates[0] != (coderateX1024/1024):\n", " # Coderate changed -> Recreate the LDPC codec object only\n", " print(f\"Slot {slotNo}: CQI changed -> Coderate:{coderateX1024}/1024\")\n", " ldpc = pdsch.getLdpcCodec(coderates = coderateX1024/1024)\n", " else:\n", " print(f\"Unknown report: {reportId}\")\n", "\n", " # Create a transmitted resource grid.\n", " txGrid = bwp.createGrid(channel.txAntenna.numEl)\n", " if pdsch is not None:\n", " # Create random data, LDPC encode it, and put it in the PDSCH's internal resource grid.\n", " # Then precode the PDSCH into the transmitted resource grid - txGrid.\n", " pdsch.initGrid()\n", " numBits = pdsch.getBitCapacity()[0]\n", " txBlock = random.bits(ldpc.txBlockSizes[0])\n", " rateMatchedCodeBlocks = ldpc.encode(txBlock, numBits) \n", " pdsch.setPdschData(rateMatchedCodeBlocks)\n", " pdsch.precodeTo(txGrid, precoder, pdschW)\n", " \n", " # Processing CSI-RS\n", " # Check to see if there are any CSI-RS resources scheduled for this slot. If so,\n", " # precode the CSI-RS resources into the txGrid\n", " csiRsResources = csiRsConfig.getResources()\n", " for csiSetId, setResources in csiRsResources.items():\n", " if csiSetId == sweepSet.rsId: # Beam Sweeping\n", " print(f\"Slot {slotNo}: Sending sweeping CSI-RS (Set ID:{sweepSet.rsId}, {len(setResources)} beams)\")\n", " for resourceId, (lIdx, kIdx, sweepReValues) in setResources.items():\n", " csiRs = csiRsConfig.getById(csiSetId, resourceId)\n", " pf = np.sqrt( csiRs.numPorts/(channel.txAntenna.numEl*csiRs.cdmSize)) # Power factor\n", " b = resourceId-1 # Beam Index\n", " w = sweepWs[:,b:b+1] # nt x 1\n", " # sweepReValues is a 1 x numCsiRsRE matrix. nt x 1 * 1 x numCsiRsRE = nt x numCsiRsRE\n", " txGrid[:,lIdx, kIdx] = (w * sweepReValues * pf, \"CSIRS_NZP\", resourceId)\n", " \n", " elif csiSetId == probeSet.rsId: # Beam probing:\n", " print(f\"Slot {slotNo}: Sending probing CSI-RS (Set ID:{probeSet.rsId}, {len(setResources)} beams)\")\n", " b = sweepCri-1 # Index of the best sweeping beam from CRI\n", " theta0, phi0 = sweepBeams[0][b], sweepBeams[1][b] # Best sweeping beam angles\n", " # Get a set of beam angles and precoders (weight vectors) for probing around the \n", " # best sweeping beam (theta0, phi0).\n", " probeWs, probeBeams = channel.txAntenna.getProbingBeams(theta0, phi0, \n", " len(probeSet), polStrategy='equal')\n", " for resourceId, (lIdx, kIdx, probeReValues) in setResources.items():\n", " csiRs = csiRsConfig.getById(csiSetId, resourceId)\n", " pf = np.sqrt( csiRs.numPorts/(channel.txAntenna.numEl*csiRs.cdmSize)) # Power factor\n", "\n", " b = resourceId - len(sweepSet) - 1 # Beam Index\n", " w = probeWs[:,b:b+1] # nt x 1\n", " # probeReValues is a 1 x numCsiRsRE matrix. nt x 1 * 1 x numCsiRsRE = nt x numCsiRsRE\n", " txGrid[:,lIdx, kIdx] = (w * probeReValues * pf, \"CSIRS_NZP\", resourceId)\n", "\n", " elif csiSetId == pmiSet.rsId: # CSI-RS for RI/PMI/CQI:\n", " print(f\"Slot {slotNo}: Sending PMI resources (Set ID:{pmiSet.rsId})\")\n", " # Use the beamforming vector corresponding to the best probed beam. This will also be\n", " # saved to pdschW when the report for this CSI-RS is received. pdschW is then combined with\n", " # the PMI procoder to precode PDSCH.\n", " wIdx = probeCri - len(sweepSet) - 1 # Index of the best probing beam from CRI\n", " pmiW = probeWs[:,wIdx:wIdx+1].copy() # shape: nt x 1\n", " for resourceId, (lIdx, kIdx, pmiReValues) in setResources.items():\n", " # Simulation note:\n", " # Scale the CSI-RS to keep its aggregate transmit power approximately\n", " # consistent with the PDSCH. This avoids unintentionally reducing the\n", " # effective PDSCH SNR in this simulation, where the noise variance is\n", " # derived from the average received signal power. This is a simulation\n", " # convenience only; it is **NOT** a 3GPP requirement or recommendation\n", " # and is not representative of how practical systems necessarily\n", " # implement CSI-RS transmission. (This comment also applies to the sweeping \n", " # and probing cases above)\n", " csiRs = csiRsConfig.getById(csiSetId, resourceId)\n", " pf = np.sqrt( csiRs.numPorts/(channel.txAntenna.numEl*csiRs.cdmSize)) # Power factor\n", " # pmiReValues is a nt x numCsiRsRE matrix. nt x 1 * nt x numCsiRsRE = nt x numCsiRsRE\n", " txGrid[:,lIdx, kIdx] = (pmiW * pmiReValues * pf, \"CSIRS_NZP\", resourceId)\n", "\n", " # Apply the channel model and add AWGN noise\n", " rxGrid = txGrid.applyChannel(channelMatrix) \n", " noisyRxGrid = rxGrid.addNoise(snrDb=snrDb)\n", "\n", " if pdsch is not None:\n", " # Receiver side processing of the PDSCH: equalization and LDPC decoding\n", " \n", " # effChannelMatrix, errVar = pdsch.estimateChannel(noisyRxGrid)\n", " # eqGrid, llrScales = pdsch.equalize(noisyRxGrid, effChannelMatrix, errVar)\n", " effChannelMatrix = channel.getEffChannel(channelMatrix, precoder, pdschW)\n", " eqGrid, llrScales = pdsch.equalize(noisyRxGrid, effChannelMatrix)\n", "\n", " llrs = pdsch.getLLRs(eqGrid, llrScales)\n", " decodedTxBlocks, crcMatch = ldpc.decode(llrs)\n", " print(f\"Slot {slotNo}: TxBlock CRC Match: {crcMatch[0][0]}\")\n", " failedSlots += 1-int(crcMatch[0][0])\n", "\n", " # UE processing of the received resource grid to generate reports\n", " csiReportMan.processRxGrid(noisyRxGrid, csiRsResources)\n", "\n", " # Go to the next channel instance for the next slot\n", " channel.goNext()\n", "print(f\"{failedSlots} of {numSlots} slots failed.\")\n" ] }, { "cell_type": "code", "execution_count": null, "id": "c465c7f2-fe28-4d29-b98c-ea4f7e64dfd0", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "5b961ba2-849f-4ab8-aa89-bd1ebbb4057f", "metadata": {}, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "id": "958d8b46-9fb6-43ec-8e30-3bf0577b808a", "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 }