5G-LENA nr-v3.3-81-g75c7590d
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-pm-search-full.cc
1// Copyright (c) 2024 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "nr-pm-search-full.h"
6
7#include <ns3/boolean.h>
8#include <ns3/simulator.h>
9#include <ns3/uinteger.h>
10
11#include <numeric>
12
13namespace ns3
14{
15
16NS_LOG_COMPONENT_DEFINE("NrPmSearchFull");
17NS_OBJECT_ENSURE_REGISTERED(NrPmSearchFull);
18
19TypeId
21{
22 static TypeId tid = TypeId("ns3::NrPmSearchFull")
23 .SetParent<NrPmSearch>()
24 .AddConstructor<NrPmSearchFull>()
25 .AddAttribute("CodebookType",
26 "Codebook class to be used",
27 TypeIdValue(NrCbTwoPort::GetTypeId()),
28 MakeTypeIdAccessor(&NrPmSearchFull::SetCodebookTypeId),
29 MakeTypeIdChecker());
30 return tid;
31}
32
33void
35{
36 m_cbFactory.SetTypeId(typeId);
37}
38
39void
40NrPmSearchFull::SetCodebookAttribute(const std::string& attrName, const AttributeValue& attrVal)
41{
42 NS_LOG_FUNCTION(this);
43 m_cbFactory.Set(attrName, attrVal);
44}
45
46void
48{
49 m_cbFactory.Set("N1", UintegerValue(m_nGnbHPorts));
50 m_cbFactory.Set("N2", UintegerValue(m_nGnbVPorts));
51 m_cbFactory.Set("IsDualPol", BooleanValue(m_isGnbDualPol));
52
53 auto maxRank = std::min({m_nRxPorts, m_nGnbPorts, static_cast<size_t>(m_rankLimit)});
54 m_ranks.resize(maxRank);
55 std::iota(m_ranks.begin(), m_ranks.end(), 1); // Fill ranks vector starting at 1
56 m_rankParams.resize(maxRank + 1);
57 for (auto rank : m_ranks)
58 {
59 m_cbFactory.Set("Rank", UintegerValue(rank));
60 m_rankParams[rank].cb = m_cbFactory.Create<NrCbTypeOne>();
61 m_rankParams[rank].cb->Init();
62 }
63}
64
67{
68 NS_LOG_FUNCTION(this);
69
70 // Extract parameters from received signal
71 auto nRows = rxSignalRb.m_chanMat.GetNumRows();
72 auto nCols = rxSignalRb.m_chanMat.GetNumCols();
73 NS_ASSERT_MSG(nRows == m_nRxPorts, "Channel mat has {} rows but UE has {} ports");
74 NS_ASSERT_MSG(nCols == m_nGnbPorts, "Channel mat has {} cols but gNB has {} ports");
75
76 // Compute the interference-normalized channel matrix
77 auto rbNormChanMat = rxSignalRb.m_covMat.CalcIntfNormChannel(rxSignalRb.m_chanMat);
78
79 // Update optimal precoding matrices based on received signal, if update is requested
80 ConditionallyUpdatePrecoding(rbNormChanMat, pmiUpdate);
81
82 // Iterate over the ranks, apply the optimal precoding matrix, create CQI message with TB size
83 auto optPrecForRanks = std::vector<PmCqiInfo>{};
84 for (auto rank : m_ranks)
85 {
86 auto cqiMsg = CreateCqiForRank(rank, rbNormChanMat);
87 optPrecForRanks.emplace_back(std::move(cqiMsg));
88 // Skip higher ranks when the current is incapable of maintaining the connection
89 if (optPrecForRanks.back().m_wbCqi == 0)
90 {
91 if (optPrecForRanks.size() >= 2)
92 {
93 optPrecForRanks.pop_back();
94 }
95 break;
96 }
97 }
98
99 // Find the rank which results in largest expected TB size, and return corresponding CQI/PMI
100 auto optRankCqiMsg = *std::max_element(
101 optPrecForRanks.begin(),
102 optPrecForRanks.end(),
103 [](const PmCqiInfo& a, const PmCqiInfo& b) { return a.m_tbSize < b.m_tbSize; });
104 return optRankCqiMsg;
105}
106
107void
109 PmiUpdate pmiUpdate)
110{
111 if (pmiUpdate.updateWb)
112 {
113 UpdateAllPrecoding(rbNormChanMat);
114 }
115 else if (pmiUpdate.updateSb)
116 {
117 UpdateSubbandPrecoding(rbNormChanMat);
118 }
119}
120
121void
123{
124 // Compute downsampled channel per subband
125 auto sbNormChanMat = SubbandDownsampling(rbNormChanMat);
126
127 for (auto rank : m_ranks)
128 {
129 // Loop over wideband precoding matrices W1 (index i1).
130 std::vector<Ptr<PrecMatParams>> optSubbandPrecoders{};
131 auto numI1 = m_rankParams[rank].cb->GetNumI1();
132 for (auto i1 = size_t{0}; i1 < numI1; i1++)
133 {
134 // Find the optimal subband PMI values (i2) for this particular i1
135 auto subbandParams = FindOptSubbandPrecoding(sbNormChanMat, i1, rank);
136
137 // Store the parameters for this wideband index i1
138 optSubbandPrecoders.emplace_back(subbandParams);
139 }
140
141 // Find the optimal wideband PMI i1
142 m_rankParams[rank].precParams =
143 *std::max_element(optSubbandPrecoders.begin(),
144 optSubbandPrecoders.end(),
145 [](const Ptr<PrecMatParams>& a, const Ptr<PrecMatParams>& b) {
146 return a->perfMetric < b->perfMetric;
147 });
148 }
149}
150
151void
153{
154 // Compute downsampled channel per subband
155 auto sbNormChanMat = SubbandDownsampling(rbNormChanMat);
156 for (auto rank : m_ranks)
157 {
158 // Recompute the best subband precoding (W2) for previously found W1 and store results
159 auto& optPrec = m_rankParams[rank].precParams;
160 NS_ASSERT(optPrec);
161 auto wbPmi = optPrec->wbPmi;
162 optPrec = FindOptSubbandPrecoding(sbNormChanMat, wbPmi, rank);
163 }
164}
165
167NrPmSearchFull::CreateCqiForRank(uint8_t rank, const NrIntfNormChanMat& rbNormChanMat) const
168{
169 // Get the previously computed optimal precoding matrix for this rank
170 auto optPrec = m_rankParams[rank].precParams;
171 NS_ASSERT_MSG(optPrec, "Tried to create a CQI message but precoding matrix does not exist");
172
173 // Upsample/convert subband precoding matrix to full RB size (size of rbNormChanMat)
174 auto rbPrecMat = SubbandUpsampling(optPrec->sbPrecMat, rbNormChanMat.GetNumPages());
175
176 // Recompute SINR value for current channel (for all RBs)
177 auto sinrMat = rbNormChanMat.ComputeSinrForPrecoding(rbPrecMat);
178
179 // For the optimal precoding matrix, determine the achievable TB size and TBLER.
180 auto mcsParams = m_amc->GetMaxMcsParams(sinrMat, m_subbandSize);
181
182 // Clamp sub-band CQI according to 3GPP 2-bit overhead limit
184 {
185 for (auto& sbCqi : mcsParams.sbCqis)
186 {
187 auto diff = (int)sbCqi - (int)mcsParams.wbCqi;
188 if (diff > 2)
189 {
190 sbCqi = mcsParams.wbCqi + 2;
191 }
192 else if (diff < -1)
193 {
194 sbCqi = mcsParams.wbCqi - 1;
195 }
196 }
197 }
198
199 // Store parameters for this rank
200 auto cqiMsg = PmCqiInfo{
201 .m_mcs = mcsParams.mcs,
202 .m_rank = rank,
203 .m_wbPmi = optPrec->wbPmi,
204 .m_wbCqi = mcsParams.wbCqi,
205 .m_sbCqis = mcsParams.sbCqis,
206 .m_sbPmis = optPrec->sbPmis,
207 .m_optPrecMat = Create<const ComplexMatrixArray>(std::move(rbPrecMat)),
208 .m_tbSize = mcsParams.tbSize,
209 };
210 return cqiMsg;
211}
212
213Ptr<NrPmSearchFull::PrecMatParams>
215 size_t i1,
216 uint8_t rank) const
217{
218 // Create the possible subband precoding matrices for each value of i2, and compute the
219 // corresponding performance metric (channel capacity) for each subband and each i2.
220 auto nSubbands = sbNormChanMat.GetNumPages();
221 auto allPrecMats = CreateSubbandPrecoders(i1, rank, nSubbands);
222 auto subbandMetricForPrec = ComputeCapacityForPrecoders(sbNormChanMat, allPrecMats);
223 auto numI2 = allPrecMats.size();
224
225 // For each subband, find the optimal value of i2 (subband PMI value)
226 auto sbPmis = std::vector<size_t>(nSubbands);
227 auto optSubbandMetric = DoubleMatrixArray{nSubbands};
228 auto optPrecMat = allPrecMats[0]; // Initialize optimal precoding matrix
229 for (auto iSb = size_t{0}; iSb < nSubbands; iSb++)
230 {
231 // Find the optimal value of i2 (subband PMI value) for the current subband
232 for (auto i2 = size_t{0}; i2 < numI2; i2++)
233 {
234 if (subbandMetricForPrec(iSb, i2) > optSubbandMetric(iSb))
235 {
236 sbPmis[iSb] = i2;
237 optSubbandMetric(iSb) = subbandMetricForPrec(iSb, i2);
238 }
239 }
240 // Store the optimal precoding matrix for this subband
241 for (size_t i = 0; i < optPrecMat.GetNumRows(); i++)
242 {
243 for (size_t j = 0; j < optPrecMat.GetNumCols(); j++)
244 {
245 optPrecMat(i, j, iSb) = allPrecMats[sbPmis[iSb]](i, j, iSb);
246 }
247 }
248 }
249 auto widebandMetric = optSubbandMetric.GetValues().sum();
250
251 auto res = Create<NrPmSearchFull::PrecMatParams>();
252 res->wbPmi = i1;
253 res->sbPmis = sbPmis;
254 res->sbPrecMat = optPrecMat;
255 res->perfMetric = widebandMetric;
256 return res;
257}
258
259std::vector<ComplexMatrixArray>
260NrPmSearchFull::CreateSubbandPrecoders(size_t i1, uint8_t rank, size_t nSubbands) const
261{
262 const auto& cb = m_rankParams[rank].cb;
263 auto numI2 = cb->GetNumI2();
264
265 std::vector<ComplexMatrixArray> allPrecMats;
266
267 for (auto i2 = size_t{0}; i2 < numI2; i2++)
268 {
269 auto basePrecMat = cb->GetBasePrecMat(i1, i2);
270 auto sbPrecMat = ExpandPrecodingMatrix(basePrecMat, nSubbands);
271 allPrecMats.emplace_back(sbPrecMat);
272 }
273 return allPrecMats;
274}
275
276ComplexMatrixArray
277NrPmSearchFull::ExpandPrecodingMatrix(ComplexMatrixArray basePrecMat, size_t nSubbands)
278{
279 NS_ASSERT_MSG(basePrecMat.GetNumPages() == 1, "Expanding to 3D requires a 2D input");
280 auto nRows = basePrecMat.GetNumRows();
281 auto nCols = basePrecMat.GetNumCols();
282 ComplexMatrixArray res{nRows, nCols, nSubbands};
283 for (size_t p = 0; p < nSubbands; p++)
284 {
285 for (size_t i = 0; i < nRows; i++)
286 {
287 for (size_t j = 0; j < nCols; j++)
288 {
289 res(i, j, p) = basePrecMat(i, j);
290 }
291 }
292 }
293 return res;
294}
295
296DoubleMatrixArray
298 std::vector<ComplexMatrixArray> allPrecMats) const
299{
300 auto nSubbands = sbNormChanMat.GetNumPages();
301 auto numI2 = allPrecMats.size();
302 // Loop over subband PMI value i2 and store the capacity for each subband and each i2
303 DoubleMatrixArray subbandCap{nSubbands, numI2};
304 for (auto i2 = size_t{0}; i2 < numI2; i2++)
305 {
306 const auto& sbPrecMat = allPrecMats[i2];
307 auto sinr = sbNormChanMat.ComputeSinrForPrecoding(sbPrecMat);
308 for (auto iSb = size_t{0}; iSb < nSubbands; iSb++)
309 {
310 double currCap = 0;
311 for (size_t iLayer = 0; iLayer < sinr.GetNumRows(); iLayer++)
312 {
313 currCap += log2(1.0 + sinr(iLayer, iSb));
314 }
315 subbandCap(iSb, i2) = currCap;
316 }
317 }
318 return subbandCap;
319}
320
321} // 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.
virtual NrIntfNormChanMat SubbandUpsampling(const NrIntfNormChanMat &precMat, size_t numPrbs) const
Upsample the input per-subband precoding matrix into a per-PRB precoding matrix.
bool m_isGnbDualPol
True when gNB has a dual-polarized antenna array.
virtual NrIntfNormChanMat SubbandDownsampling(const NrIntfNormChanMat &channelMatrix)
Downsample the input channel matrix into bins of at most m_subbandSize PRBs.
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.