5G-LENA nr-v3.0-32-g83aee33
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-pm-search-full.cc
1/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2
3// Copyright (c) 2024 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
4//
5// SPDX-License-Identifier: GPL-2.0-only
6
7#include "nr-pm-search-full.h"
8
9#include <ns3/boolean.h>
10#include <ns3/simulator.h>
11#include <ns3/uinteger.h>
12
13#include <numeric>
14
15namespace ns3
16{
17
18NS_LOG_COMPONENT_DEFINE("NrPmSearchFull");
19NS_OBJECT_ENSURE_REGISTERED(NrPmSearchFull);
20
21TypeId
23{
24 static TypeId tid = TypeId("ns3::NrPmSearchFull")
25 .SetParent<NrPmSearch>()
26 .AddConstructor<NrPmSearchFull>()
27 .AddAttribute("NrPmSearchFull::CodebookType",
28 "Codebook class to be used",
29 TypeIdValue(NrCbTwoPort::GetTypeId()),
30 MakeTypeIdAccessor(&NrPmSearchFull::SetCodebookTypeId),
31 MakeTypeIdChecker());
32 return tid;
33}
34
35void
37{
38 m_cbFactory.SetTypeId(typeId);
39}
40
41void
42NrPmSearchFull::SetCodebookAttribute(const std::string& attrName, const AttributeValue& attrVal)
43{
44 NS_LOG_FUNCTION(this);
45 m_cbFactory.Set(attrName, attrVal);
46}
47
48void
50{
51 m_cbFactory.Set("N1", UintegerValue(m_nGnbHPorts));
52 m_cbFactory.Set("N2", UintegerValue(m_nGnbVPorts));
53 m_cbFactory.Set("IsDualPol", BooleanValue(m_isGnbDualPol));
54
55 auto maxRank = std::min({m_nRxPorts, m_nGnbPorts, static_cast<size_t>(m_rankLimit)});
56 m_ranks.resize(maxRank);
57 std::iota(m_ranks.begin(), m_ranks.end(), 1); // Fill ranks vector starting at 1
58 m_rankParams.resize(maxRank + 1);
59 for (auto rank : m_ranks)
60 {
61 m_cbFactory.Set("Rank", UintegerValue(rank));
62 m_rankParams[rank].cb = m_cbFactory.Create<NrCbTypeOne>();
63 m_rankParams[rank].cb->Init();
64 }
65}
66
69{
70 NS_LOG_FUNCTION(this);
71
72 // Extract parameters from received signal
73 auto nRows = rxSignalRb.m_chanMat.GetNumRows();
74 auto nCols = rxSignalRb.m_chanMat.GetNumCols();
75 NS_ASSERT_MSG(nRows == m_nRxPorts, "Channel mat has {} rows but UE has {} ports");
76 NS_ASSERT_MSG(nCols == m_nGnbPorts, "Channel mat has {} cols but gNB has {} ports");
77
78 // Compute the interference-normalized channel matrix
79 auto rbNormChanMat = rxSignalRb.m_covMat.CalcIntfNormChannel(rxSignalRb.m_chanMat);
80
81 // Update optimal precoding matrices based on received signal, if update is requested
82 ConditionallyUpdatePrecoding(rbNormChanMat, pmiUpdate);
83
84 // Iterate over the ranks, apply the optimal precoding matrix, create CQI message with TB size
85 auto optPrecForRanks = std::vector<PmCqiInfo>{};
86 for (auto rank : m_ranks)
87 {
88 auto cqiMsg = CreateCqiForRank(rank, rbNormChanMat);
89 optPrecForRanks.emplace_back(std::move(cqiMsg));
90 }
91
92 // Find the rank which results in largest expected TB size, and return corresponding CQI/PMI
93 auto optRankCqiMsg = *std::max_element(
94 optPrecForRanks.begin(),
95 optPrecForRanks.end(),
96 [](const PmCqiInfo& a, const PmCqiInfo& b) { return a.m_tbSize < b.m_tbSize; });
97 return optRankCqiMsg;
98}
99
100void
102 PmiUpdate pmiUpdate)
103{
104 if (pmiUpdate.updateWb)
105 {
106 UpdateAllPrecoding(rbNormChanMat);
107 }
108 else if (pmiUpdate.updateSb)
109 {
110 UpdateSubbandPrecoding(rbNormChanMat);
111 }
112}
113
114void
116{
117 NS_ASSERT(m_subbandSize == 1); // TODO: enable compression from RB to subband and remove
118 // auto sbNormChanMat = rbNormChanMat; // TODO: enable compression from RB to subband
119 for (auto rank : m_ranks)
120 {
121 // Loop over wideband precoding matrices W1 (index i1).
122 std::vector<Ptr<PrecMatParams>> optSubbandPrecoders{};
123 auto numI1 = m_rankParams[rank].cb->GetNumI1();
124 for (auto i1 = size_t{0}; i1 < numI1; i1++)
125 {
126 // Find the optimal subband PMI values (i2) for this particular i1
127 auto subbandParams = FindOptSubbandPrecoding(rbNormChanMat, i1, rank);
128
129 // Store the parameters for this wideband index i1
130 optSubbandPrecoders.emplace_back(subbandParams);
131 }
132
133 // Find the optimal wideband PMI i1
134 m_rankParams[rank].precParams =
135 *std::max_element(optSubbandPrecoders.begin(),
136 optSubbandPrecoders.end(),
137 [](const Ptr<PrecMatParams>& a, const Ptr<PrecMatParams>& b) {
138 return a->perfMetric < b->perfMetric;
139 });
140 }
141}
142
143void
145{
146 NS_ASSERT(m_subbandSize == 1); // TODO: enable compression from RB to subband and remove
147 // auto sbNormChanMat = rbNormChanMat; // TODO: enable compression from RB to subband
148 for (auto rank : m_ranks)
149 {
150 // Recompute the best subband precoding (W2) for previously found W1 and store results
151 auto& optPrec = m_rankParams[rank].precParams;
152 NS_ASSERT(optPrec);
153 auto wbPmi = optPrec->wbPmi;
154 optPrec = FindOptSubbandPrecoding(rbNormChanMat, wbPmi, rank);
155 }
156}
157
159NrPmSearchFull::CreateCqiForRank(uint8_t rank, const NrIntfNormChanMat& rbNormChanMat) const
160{
161 // Get the previously computed optimal precoding matrix for this rank
162 auto optPrec = m_rankParams[rank].precParams;
163 NS_ASSERT_MSG(optPrec, "Tried to create a CQI message but precoding matrix does not exist");
164
165 // TODO: Upsample/convert subband precoding matrix to full RB size (size of rbNormChanMat)
166 auto rbPrecMat = optPrec->sbPrecMat;
167
168 // Recompute SINR value for current channel (for all RBs)
169 auto sinrMat = rbNormChanMat.ComputeSinrForPrecoding(rbPrecMat);
170
171 // For the optimal precoding matrix, determine the achievable TB size and TBLER.
172 auto mcsParams = m_amc->GetMaxMcsParams(sinrMat, m_subbandSize);
173
174 // Store parameters for this rank
175 auto cqiMsg = PmCqiInfo{
176 .m_mcs = mcsParams.mcs,
177 .m_rank = rank,
178 .m_wbPmi = optPrec->wbPmi,
179 .m_wbCqi = mcsParams.wbCqi,
180 .m_sbCqis = mcsParams.sbCqis,
181 .m_sbPmis = optPrec->sbPmis,
182 .m_optPrecMat = Create<const ComplexMatrixArray>(std::move(rbPrecMat)),
183 .m_tbSize = mcsParams.tbSize,
184 };
185 return cqiMsg;
186}
187
188Ptr<NrPmSearchFull::PrecMatParams>
190 size_t i1,
191 uint8_t rank) const
192{
193 // Create the possible subband precoding matrices for each value of i2, and compute the
194 // corresponding performance metric (channel capacity) for each subband and each i2.
195 auto nSubbands = sbNormChanMat.GetNumPages();
196 auto allPrecMats = CreateSubbandPrecoders(i1, rank, nSubbands);
197 auto subbandMetricForPrec = ComputeCapacityForPrecoders(sbNormChanMat, allPrecMats);
198 auto numI2 = allPrecMats.size();
199
200 // For each subband, find the optimal value of i2 (subband PMI value)
201 auto sbPmis = std::vector<size_t>(nSubbands);
202 auto optSubbandMetric = DoubleMatrixArray{nSubbands};
203 auto optPrecMat = allPrecMats[0]; // Initialize optimal precoding matrix
204 for (auto iSb = size_t{0}; iSb < nSubbands; iSb++)
205 {
206 // Find the optimal value of i2 (subband PMI value) for the current subband
207 for (auto i2 = size_t{0}; i2 < numI2; i2++)
208 {
209 if (subbandMetricForPrec(iSb, i2) > optSubbandMetric(iSb))
210 {
211 sbPmis[iSb] = i2;
212 optSubbandMetric(iSb) = subbandMetricForPrec(iSb, i2);
213 }
214 }
215 // Store the optimal precoding matrix for this subband
216 for (size_t i = 0; i < optPrecMat.GetNumRows(); i++)
217 {
218 for (size_t j = 0; j < optPrecMat.GetNumCols(); j++)
219 {
220 optPrecMat(i, j, iSb) = allPrecMats[sbPmis[iSb]](i, j, iSb);
221 }
222 }
223 }
224 auto widebandMetric = optSubbandMetric.GetValues().sum();
225
226 auto res = Create<NrPmSearchFull::PrecMatParams>();
227 res->wbPmi = i1;
228 res->sbPmis = sbPmis;
229 res->sbPrecMat = optPrecMat;
230 res->perfMetric = widebandMetric;
231 return res;
232}
233
234std::vector<ComplexMatrixArray>
235NrPmSearchFull::CreateSubbandPrecoders(size_t i1, uint8_t rank, size_t nSubbands) const
236{
237 const auto& cb = m_rankParams[rank].cb;
238 auto numI2 = cb->GetNumI2();
239
240 std::vector<ComplexMatrixArray> allPrecMats;
241
242 for (auto i2 = size_t{0}; i2 < numI2; i2++)
243 {
244 auto basePrecMat = cb->GetBasePrecMat(i1, i2);
245 auto sbPrecMat = ExpandPrecodingMatrix(basePrecMat, nSubbands);
246 allPrecMats.emplace_back(sbPrecMat);
247 }
248 return allPrecMats;
249}
250
251ComplexMatrixArray
252NrPmSearchFull::ExpandPrecodingMatrix(ComplexMatrixArray basePrecMat, size_t nSubbands)
253{
254 NS_ASSERT_MSG(basePrecMat.GetNumPages() == 1, "Expanding to 3D requires a 2D input");
255 auto nRows = basePrecMat.GetNumRows();
256 auto nCols = basePrecMat.GetNumCols();
257 ComplexMatrixArray res{nRows, nCols, nSubbands};
258 for (size_t p = 0; p < nSubbands; p++)
259 {
260 for (size_t i = 0; i < nRows; i++)
261 {
262 for (size_t j = 0; j < nCols; j++)
263 {
264 res(i, j, p) = basePrecMat(i, j);
265 }
266 }
267 }
268 return res;
269}
270
271DoubleMatrixArray
273 std::vector<ComplexMatrixArray> allPrecMats) const
274{
275 auto nSubbands = sbNormChanMat.GetNumPages();
276 auto numI2 = allPrecMats.size();
277 // Loop over subband PMI value i2 and store the capacity for each subband and each i2
278 DoubleMatrixArray subbandCap{nSubbands, numI2};
279 for (auto i2 = size_t{0}; i2 < numI2; i2++)
280 {
281 const auto& sbPrecMat = allPrecMats[i2];
282 auto sinr = sbNormChanMat.ComputeSinrForPrecoding(sbPrecMat);
283 for (auto iSb = size_t{0}; iSb < nSubbands; iSb++)
284 {
285 double currCap = 0;
286 for (size_t iLayer = 0; iLayer < sinr.GetNumRows(); iLayer++)
287 {
288 currCap += log2(1.0 + sinr(iLayer, iSb));
289 }
290 subbandCap(iSb, i2) = currCap;
291 }
292 }
293 return subbandCap;
294}
295
296} // namespace ns3
static TypeId GetTypeId()
Get TypeId.
Wrapper class for implementations of Type-I precoding matrices in 3GPP TS 38.214. A separate object m...
virtual void Init()=0
Initialize the codebook parameters after construction, based on attribute values.
virtual NrIntfNormChanMat CalcIntfNormChannel(const ComplexMatrixArray &chanMat) const
Calculate the interference-normalized channel matrix for SISO and MIMO. See NrIntfNormChanMat for det...
virtual NrSinrMatrix ComputeSinrForPrecoding(const ComplexMatrixArray &precMats) const
Compute the MIMO SINR when a specific precoder is applied.
PmCqiInfo CreateCqiForRank(uint8_t rank, const NrIntfNormChanMat &rbNormChanMat) const
Create CQI feedback message for a particular rank.
PmCqiInfo CreateCqiFeedbackMimo(const NrMimoSignal &rxSignalRb, PmiUpdate pmiUpdate) override
Create CQI feedback with optimal rank, optimal PMI, and corresponding CQI values. Optimal rank is con...
std::vector< RankParams > m_rankParams
The parameters (PMI values, codebook) for each rank.
void UpdateAllPrecoding(const NrIntfNormChanMat &rbNormChanMat)
For all ranks, update the optimum precoding matrices (wideband and subband).
Ptr< PrecMatParams > FindOptSubbandPrecoding(const NrIntfNormChanMat &sbNormChanMat, size_t i1, uint8_t rank) const
Find the optimal subband precoding matrix for the given wideband precoding.
ObjectFactory m_cbFactory
The factory used to create the codebooks.
void SetCodebookTypeId(const TypeId &typeId)
Set the TypeId of the codebook (NrCbTypeOne) to be used.
static TypeId GetTypeId()
Get TypeId.
void InitCodebooks() override
Create and initialize the codebook for each rank.
void ConditionallyUpdatePrecoding(const NrIntfNormChanMat &rbNormChanMat, PmiUpdate pmiUpdate)
Update the WB and/or SB PMI, or neither.
std::vector< ComplexMatrixArray > CreateSubbandPrecoders(size_t i1, uint8_t rank, size_t nSubbands) const
Create the subband precoding matrices for the given wideband precoding.
void UpdateSubbandPrecoding(const NrIntfNormChanMat &rbNormChanMat)
For all ranks, update the opt subband PMI assuming previous value of wideband PMI.
DoubleMatrixArray ComputeCapacityForPrecoders(const NrIntfNormChanMat &sbNormChanMat, std::vector< ComplexMatrixArray > allPrecMats) const
Compute the Shannon capacity for each possible precoding matrix in each subband.
void SetCodebookAttribute(const std::string &attrName, const AttributeValue &attrVal)
Set the ns-3 attribute of the codebook (NrCbTypeOne).
Base class for searching optimal precoding matrices and creating full CQI/PMI feedback This is a most...
uint8_t m_rankLimit
Limit the UE's maximum supported rank.
size_t m_nRxPorts
Number of receive ports at this UE.
size_t m_nGnbHPorts
Number of horizontal ports in the gNB antenna array.
Ptr< const NrAmc > m_amc
The NrAmc to be used for computing TB size and MCS.
size_t m_nGnbPorts
Total number of ports in the gNB antenna array.
std::vector< uint8_t > m_ranks
The set of ranks for which to compute precoding matrices.
bool m_isGnbDualPol
True when gNB has a dual-polarized antenna array.
size_t m_subbandSize
Size of each subband (in number of RBs)
size_t m_nGnbVPorts
Number of vertical ports in the gNB antenna array.
Helper struct for processing and storing received signals for use in CSI feedback.
NrCovMat m_covMat
Interference and noise covariance matrix; nRxPorts * nRxPorts * nRbs.
ComplexMatrixArray m_chanMat
Channel Matrix; nRxPorts * nTxPorts * nRbs.
Parameters that define if PMI should be updated or if previous PMI values are used.
bool updateWb
Defines whether to update WB PMI.
bool updateSb
Defines whether to update SB PMI.
The structure used for the CQI feedback message that contains the optimum CQI, RI,...
uint8_t m_mcs
Modulation and coding scheme supported by current channel.