5G-LENA nr-v4.0
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-initial-association.cc
1// Copyright (c) 2020 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "nr-initial-association.h"
6
7#include "beamforming-vector.h"
8
9#include "ns3/nr-module.h"
10#include "ns3/object.h"
11
12#include <numeric>
13
14namespace ns3
15{
16NS_LOG_COMPONENT_DEFINE("NrInitialAssociation");
17NS_OBJECT_ENSURE_REGISTERED(NrInitialAssociation);
18
19using LocalSearchParams = NrInitialAssociation::LocalSearchParams;
20
21TypeId
23{
24 static TypeId tid =
25 TypeId("ns3::NrInitialAssociation")
26 .SetParent<Object>()
27 .SetGroupName("Initial Access")
28 .AddConstructor<NrInitialAssociation>()
29 .AddAttribute(
30 "HandoffMargin",
31 "handoff margin (dB); UE attaches to any gNB whose RSRP is within hand off margin",
32 DoubleValue(0),
35 MakeDoubleChecker<double>())
36 .AddAttribute("PrimaryCarrierIndex",
37 "primary carrier index",
38 DoubleValue(0),
41 MakeDoubleChecker<double>())
42
43 .AddAttribute("NumMainInterfererGnb",
44 "Number of main interferer gNBs",
45 UintegerValue(6),
48 MakeDoubleChecker<double>());
49
50 return tid;
51}
52
53void
55{
56 m_numMainInterfererGnb = numInterfere;
57}
58
59uint8_t
61{
62 return m_numMainInterfererGnb;
63}
64
65Ptr<NetDevice>
67{
68 NS_ASSERT(m_associatedGnb != nullptr);
69 return m_associatedGnb;
70}
71
72NetDeviceContainer
74{
75 return m_intfGnbDevs;
76}
77
78double
80{
81 return m_maxRsrps[gnbId];
82}
83
86{
87 return m_bestBfVectors[gnbId];
88}
89
90double
92{
93 return m_rsrpRatio;
94}
95
96void
98{
99 m_startSsb = startSsb;
100}
101
102void
104{
105 m_numBandsSsb = numSsbRb;
106}
107
108bool
110{
111 NS_ASSERT_MSG(m_freq, "Error freq in initial association must set first");
112
113 auto numGnbBeams = m_colBeamAngles.size() * m_rowBeamAngles.size();
114 if (m_freq <= 3e9)
115 {
116 return (numGnbBeams <= 4);
117 }
118 else if (m_freq <= 6e9)
119 {
120 return (numGnbBeams <= 8);
121 }
122 else
123 {
124 return (numGnbBeams <= 64);
125 }
126}
127
128void
130{
131 m_handoffMargin = margin;
132}
133
134double
136{
137 return m_freq;
138}
139
140double
142{
143 return m_handoffMargin;
144}
145
146std::vector<double>
148{
149 return m_rowBeamAngles;
150}
151
152void
153NrInitialAssociation::SetRowBeamAngles(std::vector<double> rowBfVect)
154{
155 m_rowBeamAngles = rowBfVect;
156}
157
158std::vector<double>
160{
161 return m_colBeamAngles;
162}
163
164void
165NrInitialAssociation::SetColBeamAngles(std::vector<double> colBfVect)
166{
167 m_colBeamAngles = colBfVect;
168}
169
170Ptr<const NetDevice>
172{
173 return m_ueDevice;
174}
175
176void
177NrInitialAssociation::SetUeDevice(const Ptr<NetDevice>& ueDev)
178{
179 m_ueDevice = ueDev;
180}
181
182void
183NrInitialAssociation::SetGnbDevices(const NetDeviceContainer& gnbDevices)
184{
185 m_gnbDevices = gnbDevices;
186}
187
188void
190{
191 m_primaryCarrierIndex = index;
192}
193
194double
196{
197 return m_primaryCarrierIndex;
198}
199
201NrInitialAssociation::ExtractUeParameters() const
202{
203 auto ueDev = m_ueDevice->GetObject<NrUeNetDevice>();
204 auto phy = ueDev->GetPhy(m_primaryCarrierIndex);
205 auto spectrumPhy = phy->GetSpectrumPhy();
206 auto spectrumChannel = spectrumPhy->GetSpectrumChannel();
207 auto spectrumPropModel = StaticCast<ThreeGppSpectrumPropagationLossModel>(
208 spectrumChannel->GetPhasedArraySpectrumPropagationLossModel());
209 AntennaArrayModels antModel;
210 for (uint8_t i = 0; i < spectrumPhy->GetNumPanels(); i++)
211 {
212 auto bPhasedArrayModel = spectrumPhy->GetPanelByIndex(i)->GetObject<PhasedArrayModel>();
213 antModel.ueArrayModel.push_back(
214 Copy<UniformPlanarArray>(DynamicCast<UniformPlanarArray>(bPhasedArrayModel)));
215 antModel.ueArrayModel[i]->SetNumVerticalPorts(bPhasedArrayModel->GetNumRows());
216 antModel.ueArrayModel[i]->SetNumHorizontalPorts(bPhasedArrayModel->GetNumColumns());
217 }
218
219 auto channel =
220 StaticCast<ThreeGppSpectrumPropagationLossModel>(spectrumPropModel)->GetChannelModel();
221 ChannelParams chParams;
222 chParams.channelModel = StaticCast<ThreeGppChannelModel>(channel);
223 chParams.spectrumPropModel = spectrumPropModel;
224 NS_ASSERT_MSG(chParams.channelModel != nullptr,
225 "Channel Model should be to be ThreeGppChannelModel");
226
227 Mobilities mobility;
228 mobility.ueMobility = spectrumPhy->GetMobility();
229 chParams.pathLossModel =
230 StaticCast<ThreeGppPropagationLossModel>(spectrumChannel->GetPropagationLossModel());
231 LocalSearchParams lsps{.chParams = chParams, .mobility = mobility, .antennaArrays = antModel};
232 return lsps;
233}
234
235Ptr<UniformPlanarArray>
236NrInitialAssociation::ExtractGnbParameters(const Ptr<NetDevice>& gnbDevice,
237 LocalSearchParams& lsps) const
238{
239 auto& chParams = lsps.chParams;
240 auto& mobility = lsps.mobility;
241 auto& antenna = lsps.antennaArrays;
242
243 const auto gnbDev = gnbDevice->GetObject<NrGnbNetDevice>();
244 const auto phy = gnbDev->GetPhy(m_primaryCarrierIndex);
245 const auto spectrumPhy = phy->GetSpectrumPhy();
246 chParams.spectralModel = spectrumPhy->GetRxSpectrumModel();
247 auto bPhasedArrayModel = spectrumPhy->GetAntenna()->GetObject<PhasedArrayModel>();
248
249 // Local copy of antenna model is modified so actual model used after initial access is not
250 // affected
251 antenna.gnbArrayModel =
252 Copy<UniformPlanarArray>(DynamicCast<UniformPlanarArray>(bPhasedArrayModel));
253 mobility.gnbMobility = spectrumPhy->GetMobility();
254 const auto rowElemsPerPort = antenna.gnbArrayModel->GetVElemsPerPort();
255 const auto colElemsPerPort = antenna.gnbArrayModel->GetHElemsPerPort();
256
257 // For initial access beams typically have wider beams so limit the beams to first port
258 // only.
259 // This reduces the complexity of the channel model
260 antenna.gnbArrayModel->SetNumVerticalPorts(1);
261 antenna.gnbArrayModel->SetNumHorizontalPorts(1);
262 antenna.gnbArrayModel->SetNumRows(rowElemsPerPort);
263 antenna.gnbArrayModel->SetNumColumns(colElemsPerPort);
264
265 return antenna.gnbArrayModel;
266}
267
268PhasedArrayModel::ComplexVector
269NrInitialAssociation::GenBeamforming(double angRow,
270 double angCol,
271 Ptr<UniformPlanarArray> gnbArrayModel) const
272{
273 auto bfVector = CreateKroneckerBfv(gnbArrayModel, angRow, angCol);
274 return bfVector;
275}
276
277double
278NrInitialAssociation::ComputeRxPsd(Ptr<const SpectrumSignalParameters> spectrumSigParam) const
279{
280 auto& spectrumChannelMatrix = spectrumSigParam->spectrumChannelMatrix;
281 NS_ASSERT_MSG(spectrumChannelMatrix->GetNumPages() >= m_numBandsSsb,
282 "The primary carrier bandwidth should have at least 20 PRBs to fit SSBs");
283 auto numUePorts = spectrumChannelMatrix->GetNumRows();
284 double totalPsd = 0;
285 for (size_t iRb = m_startSsb; iRb < m_startSsb + m_numBandsSsb; iRb++)
286 {
287 auto psdPerRb{0.0};
288 // Compute the PSD from the MIMO channel matrix.
289 for (size_t idxAnt = 0; idxAnt < numUePorts; idxAnt++)
290 {
291 psdPerRb += norm(spectrumChannelMatrix->Elem(idxAnt, 0, iRb));
292 }
293 totalPsd += psdPerRb;
294 }
295 return totalPsd;
296}
297
298double
299NrInitialAssociation::ComputeMaxRsrp(const Ptr<NetDevice>& gnbDevice, LocalSearchParams& lsps)
300{
301 auto& chParams = lsps.chParams;
302 auto& mobility = lsps.mobility;
303 auto& antennas = lsps.antennaArrays;
304 uint8_t activePanelIndex = GetUeActivePanel();
305 antennas.gnbArrayModel = ExtractGnbParameters(gnbDevice, lsps);
306
307 NS_ASSERT_MSG(chParams.spectralModel->GetNumBands() >= m_numBandsSsb,
308 "The primary carrier bandwidth should have at least 20 PRBs to fit SSBs");
309 std::vector<int> activeRbs;
310 for (size_t rbId = m_startSsb; rbId < m_numBandsSsb + m_startSsb; rbId++)
311 {
312 activeRbs.push_back(rbId);
313 }
314 NrAnglePair bfAngles;
315 Ptr<const SpectrumValue> fakePsd = NrSpectrumValueHelper::CreateTxPowerSpectralDensity(
316 DynamicCast<NrGnbNetDevice>(gnbDevice)->GetPhy(0)->GetTxPower(),
317 activeRbs,
318 chParams.spectralModel,
319 NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED);
320 auto txParams = Create<SpectrumSignalParameters>();
321 for (auto& i : antennas.ueArrayModel)
322 {
323 PhasedArrayModel::ComplexVector uebfVector(i->GetNumElems());
324 uebfVector[0] = 1.0;
325 i->SetBeamformingVector(uebfVector);
326 }
327
328 auto gnbTxPower = DynamicCast<NrGnbNetDevice>(gnbDevice)->GetPhy(0)->GetTxPower();
329 for (size_t k = 0; k < antennas.ueArrayModel.size(); k++)
330 {
331 for (size_t j = 0; j < m_rowBeamAngles.size(); j++)
332 {
333 for (size_t i = 0; i < m_colBeamAngles.size(); i++)
334 {
335 auto bf =
336 GenBeamforming(m_rowBeamAngles[j], m_colBeamAngles[i], antennas.gnbArrayModel);
337 antennas.gnbArrayModel->SetBeamformingVector(bf);
338 txParams->psd = Copy<SpectrumValue>(fakePsd);
339 auto rxParam = chParams.spectrumPropModel->DoCalcRxPowerSpectralDensity(
340 txParams,
341 mobility.gnbMobility,
342 mobility.ueMobility,
343 antennas.gnbArrayModel,
344 antennas.ueArrayModel[k]);
345 if (!rxParam->spectrumChannelMatrix)
346 {
347 // out-of-range (see DistanceBasedThreeGppSpectrumPropagationLossModel)
348 continue;
349 }
350 auto eng = gnbTxPower * ComputeRxPsd(rxParam);
351 if (eng > lsps.maxPsdFound)
352 {
353 lsps.maxPsdFound = eng;
354 bfAngles = {m_rowBeamAngles[j], m_colBeamAngles[i]};
355 activePanelIndex =
356 k; // active panel has to be update to K as better beam has found
357 }
358 }
359 }
360 }
361 auto attenuation =
362 chParams.pathLossModel->CalcRxPower(0, mobility.gnbMobility, mobility.ueMobility);
363 m_bestBfVectors.push_back(bfAngles);
364 SetUeActivePanel(activePanelIndex);
365 return pow(10.0, attenuation / 10.0) * lsps.maxPsdFound;
366}
367
368double
369NrInitialAssociation::ComputeRsrpRatio(const double totalRsrp, const std::vector<uint16_t> idxVal)
370{
371 NS_ASSERT_MSG(m_numIntfGnbs > 0,
372 "Number of main interfering gNBs should not be negative " << m_numIntfGnbs);
373 NS_ASSERT_MSG(m_numIntfGnbs < m_maxRsrps.size(),
374 "Number of main interfering gNBs should be less than the number of gNB ");
375 double intfRsrp = 0;
376 auto nIntf = 0;
377 auto j = m_maxRsrps.size() - nIntf;
378 while (nIntf < int(m_numIntfGnbs))
379 {
380 j--;
381 auto gnbDev = m_gnbDevices.Get(idxVal[j]);
382 if (gnbDev != m_associatedGnb)
383 {
384 m_intfGnbDevs.Add(gnbDev);
385 intfRsrp += std::pow(10.0, m_maxRsrps[idxVal[j]] / 10.0);
386 nIntf++;
387 }
388 }
389 return (totalRsrp - intfRsrp) / intfRsrp;
390}
391
392void
393NrInitialAssociation::InitializeIntfSet(uint16_t numIntf, bool useRelRsrp, double relRsrpThreshold)
394{
395 NS_ASSERT_MSG(!m_maxRsrps.empty(), "Populate RSRP values first");
396 NS_ASSERT_MSG(m_associatedGnb, "Association should be completed first");
397 m_numIntfGnbs = numIntf;
398
399 std::vector<uint16_t> idxVal(m_maxRsrps.size());
400 std::iota(idxVal.begin(), idxVal.end(), 0); // Initializing the indexes
401
402 // getting the index of the gNBs in increasing order of received power to the UE
403 std::sort(idxVal.begin(), idxVal.end(), [&](int i, int j) {
404 return (m_maxRsrps[i] < m_maxRsrps[j]);
405 });
406
407 auto cumSumIntf = GetInterference(idxVal);
408 auto totalInterference = GetTotalInterference(cumSumIntf);
409 NS_ASSERT_MSG(totalInterference > 0,
410 "Initial detected power of interferer should be greater than 0");
411
412 m_numIntfGnbs = useRelRsrp
413 ? GetNumIntfGnbsByRelRsrp(cumSumIntf, relRsrpThreshold, totalInterference)
414 : m_numIntfGnbs;
415 m_rsrpRatio = ComputeRsrpRatio(totalInterference, idxVal);
416}
417
418std::vector<double>
419NrInitialAssociation::GetInterference(const std::vector<uint16_t>& idxVal) const
420{
421 std::vector<double> cumSumIntf(m_maxRsrps.size()); // Cumulative sum of RSRP values from gNBs
422 cumSumIntf[0] = std::pow(10.0, m_maxRsrps[idxVal[0]] / 10.0);
423 for (size_t i = 1; i < m_maxRsrps.size(); ++i)
424 {
425 // Getting cumulative sum of received RSRP from gNB wherein RSRP are in increasing order
426 cumSumIntf[i] = cumSumIntf[i - 1] + std::pow(10.0, m_maxRsrps[idxVal[i]] / 10.0);
427 }
428 return cumSumIntf;
429}
430
431double
432NrInitialAssociation::GetTotalInterference(const std::vector<double>& cumSumIntf) const
433{
434 auto totalInterference =
435 cumSumIntf[m_maxRsrps.size() - 1] -
436 std::pow(10.0,
437 m_rsrpAsscGnb / 10.0); // subtract the power of the associated gNB to get
438 // overall interference. Note that because hand off
439 // margin, the associated gNB may not be the one with
440 // highest received power
441 return totalInterference;
442}
443
444size_t
445NrInitialAssociation::GetNumIntfGnbsByRelRsrp(const std::vector<double> cumSumIntf,
446 const double relRsrpThreshold,
447 const double totalInterference) const
448{
449 auto numIntfGnbs = m_maxRsrps.size() - 1;
450 for (size_t i = 0; i < m_maxRsrps.size(); ++i)
451 {
452 // This line is equivalent to cumSumIntf[i] < relPsd * Main Interference.
453 // Note that main interference is totalInterference - cumSumIntf[i]
454 if ((1 + relRsrpThreshold) * cumSumIntf[i] > relRsrpThreshold * totalInterference)
455 {
456 numIntfGnbs -= i; // -1 for the associated gNB
457 break;
458 }
459 }
460 return numIntfGnbs;
461}
462
463void
464NrInitialAssociation::PopulateRsrps(LocalSearchParams& lsps)
465{
466 // Compute maximum RSRP per each UE and all m_gnbDevices in dB
467 std::vector<double> powers;
468 powers.resize(m_gnbDevices.GetN());
469 std::transform(m_gnbDevices.Begin(),
470 m_gnbDevices.End(),
471 powers.begin(),
472 [&](const Ptr<NetDevice> gnbDev) { return ComputeMaxRsrp(gnbDev, lsps); });
473 m_maxRsrps.resize(powers.size());
474 std::transform(powers.begin(), powers.end(), m_maxRsrps.begin(), [&](const double val) {
475 return 10 * log10(val); // in dB
476 });
477}
478
479std::pair<Ptr<NetDevice>, double>
481{
482 auto localParams = ExtractUeParameters();
483 m_freq = localParams.chParams.channelModel->GetFrequency();
484 if (m_maxRsrps.empty())
485 {
486 PopulateRsrps(localParams);
487 }
488 auto maxVal = std::max_element(m_maxRsrps.begin(), m_maxRsrps.end());
489 std::vector<bool> assocFlag;
490 assocFlag.resize(m_maxRsrps.size());
491
492 // Keep gnbDevs with RSRP values lower than maxVal and handoffMargin
493 std::transform(m_maxRsrps.begin(), m_maxRsrps.end(), assocFlag.begin(), [&](const double val) {
494 return (*maxVal - val) <= m_handoffMargin;
495 });
496 auto numPossibleGnb = std::accumulate(assocFlag.begin(), assocFlag.end(), 0);
497
498 // Choose Randomly gnbDev from possible gNB
499 Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable>();
500 x->SetAttribute("Min", DoubleValue(1));
501 x->SetAttribute("Max", DoubleValue(numPossibleGnb));
502 auto value = x->GetInteger();
503 auto count = 0;
504 for (size_t i = 0; i < m_gnbDevices.GetN(); i++)
505 {
506 count = (assocFlag[i] ? count + 1 : count);
507 if (count == int(value))
508 {
509 m_associatedGnb = m_gnbDevices.Get(i);
510 m_beamformingVector = GenBeamforming(m_bestBfVectors[i].rowAng,
511 m_bestBfVectors[i].colAng,
512 localParams.antennaArrays.gnbArrayModel);
513 m_rsrpAsscGnb = m_maxRsrps[i];
514 return std::make_pair(m_associatedGnb, m_rsrpAsscGnb);
515 }
516 }
517 NS_FATAL_ERROR("Method should have returned");
518}
519
520double
522{
523 return m_rsrpAsscGnb;
524}
525
526void
528{
529 auto ueDev = m_ueDevice->GetObject<NrUeNetDevice>();
530 auto phy = ueDev->GetPhy(0);
531 auto spectrumPhy = phy->GetSpectrumPhy();
532 spectrumPhy->SetActivePanel(panelIndex);
533}
534
535uint8_t
537{
538 auto ueDev = m_ueDevice->GetObject<NrUeNetDevice>();
539 auto phy = ueDev->GetPhy(0);
540 auto spectrumPhy = phy->GetSpectrumPhy();
541 for (uint8_t i = 0; i < spectrumPhy->GetNumPanels(); i++)
542 {
543 if (spectrumPhy->GetPanelByIndex(i) == spectrumPhy->GetAntenna())
544 {
545 return i;
546 }
547 }
548 NS_ABORT_MSG("Missed the antenna panel");
549}
550
551} // namespace ns3
Ptr< const NetDevice > GetUeDevice() const
Get UE device for which initial association is required.
static TypeId GetTypeId()
Get the type ID.
void SetNumMainInterfererGnb(uint8_t numInterfere)
Set number of main interferer gNBs.
Ptr< NetDevice > GetAssociatedGnb() const
Get the gNB associated gNB with the UE.
void SetUeActivePanel(int8_t panelIndex) const
Set the active panel for the UE device in NrSpectrumPhy.
double GetAssociatedRsrp() const
Get RSRP of associated gNB.
std::vector< double > GetColBeamAngles() const
Get col angles of the beam used.
size_t GetNumIntfGnbsByRelRsrp(const std::vector< double > cumSumIntf, const double relRsrpThreshold, const double totalInterference) const
Get the number of interference gNB based on Relative RSRP value.
void SetPrimaryCarrier(double index)
Set the primary BWP or carrier.
double GetCarrierFrequency() const
Get carrier frequency.
void SetUeDevice(const Ptr< NetDevice > &ueDev)
Set UE device for which initial association is required.
uint8_t GetUeActivePanel() const
Get the index of the active panel for the UE device in NrSpectrumPhy.
void SetHandoffMargin(double margin)
Set hand off margin in dB.
void SetColBeamAngles(std::vector< double > colVect)
Set column beam angles.
NetDeviceContainer GetInterferingGnbs() const
Get the gNBs which are main interferer with the UE.
std::pair< Ptr< NetDevice >, double > FindAssociatedGnb()
Find the gNB associated with the UE.
bool CheckNumBeamsAllowed() const
Check whether number of beams is corresponds to standard.
void InitializeIntfSet(uint16_t numIntfs, bool useRelRsrp, double relRsrpThreshold)
Create a container of gNBs forming a main interfering set with the UE.
double GetTotalInterference(const std::vector< double > &cumSumIntf) const
Calculate total interference based on RSRP values from gNBs.
std::vector< double > GetInterference(const std::vector< uint16_t > &idxVal) const
Calculate the cumulative sum of RSRP values from gNBs.
double GetPrimaryCarrier() const
Get the primary BWP or carrier.
void SetRowBeamAngles(std::vector< double > rowVect)
Set row beam angles.
uint8_t GetNumMainInterfererGnb() const
Get number of main interferer gNBs.
NrAnglePair GetBestBfv(uint64_t gnbId) const
Get the best beam from a given gNB to the UE.
void SetGnbDevices(const NetDeviceContainer &gnbDevices)
Set gnb devices among which association is done.
void SetStartSsbRb(uint16_t startSsb)
Set start RB for SSB.
double GetHandoffMargin() const
Get handoff margin.
double GetMaxRsrp(uint64_t gnbId) const
Get the max RSRP from a given gNB.
double GetRelativeRsrpRatio() const
Get relative RSRP of remaining gNBs to that of the main one.
std::vector< double > GetRowBeamAngles() const
Get row angles of the beam used.
void SetNumSsbRb(uint16_t numSsbRb)
Set number of RBs for association.
static Ptr< SpectrumValue > CreateTxPowerSpectralDensity(double powerTx, const std::vector< int > &rbIndexVector, const Ptr< const SpectrumModel > &txSm, enum PowerAllocationType allocationType)
Create SpectrumValue that will represent transmit power spectral density, and assuming that all RBs a...
The User Equipment NetDevice.
Ptr< NrUePhy > GetPhy(uint8_t index) const
Obtain a pointer to the PHY at the index specified.
Angle pair in degrees for the row and column angle of beam direction for uniform planar array NrIniti...
LocalSearchParams struct format to keep ChannelParams, Mobilities, and AntennaArrayModels of UE and g...