5G-LENA nr-v3.3-120-gdac69c56
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-gnb-phy.cc
1// Copyright (c) 2019 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#define NS_LOG_APPEND_CONTEXT \
6 do \
7 { \
8 std::clog << " [ CellId " << GetCellId() << ", bwpId " << GetBwpId() << "] "; \
9 } while (false);
10
11#include "nr-gnb-phy.h"
12
13#include "beam-manager.h"
14#include "nr-ch-access-manager.h"
15#include "nr-gnb-net-device.h"
16#include "nr-net-device.h"
17#include "nr-ue-net-device.h"
18#include "nr-ue-phy.h"
19
20#include "ns3/double.h"
21#include "ns3/enum.h"
22#include "ns3/log.h"
23#include "ns3/node-list.h"
24#include "ns3/node.h"
25#include "ns3/object-vector.h"
26#include "ns3/pointer.h"
27#include "ns3/uinteger.h"
28
29#include <algorithm>
30#include <functional>
31#include <random>
32#include <string>
33#include <unordered_set>
34#include <vector>
35
36namespace ns3
37{
38
39NS_LOG_COMPONENT_DEFINE("NrGnbPhy");
40
41NS_OBJECT_ENSURE_REGISTERED(NrGnbPhy);
42
44 : m_n0Delay(0),
45 m_n1Delay(4)
46{
47 NS_LOG_FUNCTION(this);
48 m_gnbCphySapProvider = new MemberNrGnbCphySapProvider<NrGnbPhy>(this);
50}
51
55
56void
58{
59 NS_LOG_FUNCTION(this);
60 delete m_gnbCphySapProvider;
61 delete m_nrFhPhySapUser;
62 m_nrFhPhySapUser = nullptr;
63 m_nrFhPhySapProvider = nullptr;
65}
66
67void
68NrGnbPhy::EnableCsiRs()
69{
70 m_enableCsiRs = true;
71}
72
73TypeId
75{
76 static TypeId tid =
77 TypeId("ns3::NrGnbPhy")
78 .SetParent<NrPhy>()
79 .AddConstructor<NrGnbPhy>()
80 .AddAttribute("RbOverhead",
81 "Overhead when calculating the usable RB number",
82 DoubleValue(0.04),
84 MakeDoubleChecker<double>(0, 0.5))
85 .AddAttribute("TxPower",
86 "Transmission power in dBm",
87 DoubleValue(4.0),
88 MakeDoubleAccessor(&NrGnbPhy::SetTxPower, &NrGnbPhy::GetTxPower),
89 MakeDoubleChecker<double>())
90 .AddAttribute(
91 "NoiseFigure",
92 "Loss (dB) in the Signal-to-Noise-Ratio due to non-idealities in the receiver."
93 " According to Wikipedia (http://en.wikipedia.org/wiki/Noise_figure), this is "
94 "\"the difference in decibels (dB) between"
95 " the noise output of the actual receiver to the noise output of an "
96 " ideal receiver with the same overall gain and bandwidth when the receivers "
97 " are connected to sources at the standard noise temperature T0.\" "
98 "In this model, we consider T0 = 290K.",
99 DoubleValue(5.0),
100 MakeDoubleAccessor(&NrPhy::SetNoiseFigure, &NrPhy::GetNoiseFigure),
101 MakeDoubleChecker<double>())
102 .AddAttribute(
103 "PowerAllocationType",
104 "Defines the type of the power allocation. Currently are supported "
105 "two types: \"UniformPowerAllocBw\", which is a uniform power allocation over all "
106 "bandwidth (over all RBs), and \"UniformPowerAllocUsed\", which is a uniform "
107 "power allocation over used (active) RBs. By default is set a uniform power "
108 "allocation over used RBs .",
109 EnumValue(NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED),
110 MakeEnumAccessor<NrSpectrumValueHelper::PowerAllocationType>(
113 MakeEnumChecker(NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_BW,
114 "UniformPowerAllocBw",
115 NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED,
116 "UniformPowerAllocUsed"))
117 .AddAttribute("SpectrumPhy",
118 "The downlink NrSpectrumPhy associated to this NrPhy",
119 TypeId::ATTR_GET,
120 PointerValue(),
121 MakePointerAccessor(&NrPhy::GetSpectrumPhy),
122 MakePointerChecker<NrSpectrumPhy>())
123 .AddTraceSource("UlSinrTrace",
124 "UL SINR statistics.",
125 MakeTraceSourceAccessor(&NrGnbPhy::m_ulSinrTrace),
126 "ns3::UlSinr::TracedCallback")
127 .AddTraceSource("GnbPhyRxedCtrlMsgsTrace",
128 "Gnb PHY Rxed Control Messages Traces.",
129 MakeTraceSourceAccessor(&NrGnbPhy::m_phyRxedCtrlMsgsTrace),
130 "ns3::NrPhyRxTrace::RxedGnbPhyCtrlMsgsTracedCallback")
131 .AddTraceSource("GnbPhyTxedCtrlMsgsTrace",
132 "Gnb PHY Txed Control Messages Traces.",
133 MakeTraceSourceAccessor(&NrGnbPhy::m_phyTxedCtrlMsgsTrace),
134 "ns3::NrPhyRxTrace::TxedGnbPhyCtrlMsgsTracedCallback")
135 .AddAttribute("N0Delay",
136 "Minimum processing delay needed to decode DL DCI and decode DL data",
137 UintegerValue(0),
138 MakeUintegerAccessor(&NrGnbPhy::SetN0Delay, &NrGnbPhy::GetN0Delay),
139 MakeUintegerChecker<uint32_t>(0, 1))
140 .AddAttribute("N1Delay",
141 "Minimum processing delay (UE side) from the end of DL Data reception to "
142 "the earliest possible start of the corresponding ACK/NACK transmission",
143 UintegerValue(2),
144 MakeUintegerAccessor(&NrGnbPhy::SetN1Delay, &NrGnbPhy::GetN1Delay),
145 MakeUintegerChecker<uint32_t>(0, 4))
146 .AddAttribute("N2Delay",
147 "Minimum processing delay needed to decode UL DCI and prepare UL data",
148 UintegerValue(2),
149 MakeUintegerAccessor(&NrGnbPhy::SetN2Delay, &NrGnbPhy::GetN2Delay),
150 MakeUintegerChecker<uint32_t>(0, 4))
151 .AddAttribute("TbDecodeLatency",
152 "Transport block decode latency",
153 TimeValue(MicroSeconds(100)),
155 MakeTimeChecker())
156 .AddAttribute("Numerology",
157 "The 3GPP numerology to be used",
158 UintegerValue(0),
159 MakeUintegerAccessor(&NrPhy::SetNumerology, &NrPhy::GetNumerology),
160 MakeUintegerChecker<uint16_t>())
161 .AddAttribute(
162 "SymbolsPerSlot",
163 "Number of symbols in one slot",
164 UintegerValue(14),
165 MakeUintegerAccessor(&NrPhy::SetSymbolsPerSlot, &NrPhy::GetSymbolsPerSlot),
166 MakeUintegerChecker<uint16_t>())
167 .AddAttribute("Pattern",
168 "The slot pattern",
169 StringValue("F|F|F|F|F|F|F|F|F|F|"),
170 MakeStringAccessor(&NrGnbPhy::SetPattern, &NrGnbPhy::GetPattern),
171 MakeStringChecker())
172 .AddAttribute(
173 "CsiRsModel",
174 "Defines the type of the CSI-RS model to use. Currently the user can select "
175 "either: CsiRsPerUe or CsiRsPerBeam. CsiRsPerUe means that CSI-RS signals will be "
176 "transmitted towards a specific "
177 "UE periodically. CsiRsPerBeam means that the CSI-RS will be transmitted using a "
178 "predefined set of beams.",
179 EnumValue(NrGnbPhy::CSI_RS_PER_UE),
180 MakeEnumAccessor<NrGnbPhy::CsiRsModel>(&NrGnbPhy::SetCsiRsModel,
182 MakeEnumChecker(NrGnbPhy::CSI_RS_PER_UE,
183 "CsiRsPerUe",
185 "CsiRsPerBeam"))
186 .AddAttribute("CsiRsPeriodicity",
187 "Default CSI periodicity in the number of slots",
188 UintegerValue(10),
189 MakeUintegerAccessor(&NrGnbPhy::SetCsiRsPeriodicity,
191 MakeUintegerChecker<uint16_t>())
192 .AddTraceSource("SlotDataStats",
193 "Data statistics for the current slot: SfnSf, active UE, used RE, "
194 "used symbols, available RBs, available symbols, bwp ID, cell ID",
195 MakeTraceSourceAccessor(&NrGnbPhy::m_phySlotDataStats),
196 "ns3::NrGnbPhy::SlotStatsTracedCallback")
197 .AddTraceSource("SlotCtrlStats",
198 "Ctrl statistics for the current slot: SfnSf, active UE, used RE, "
199 "used symbols, available RBs, available symbols, bwp ID, cell ID",
200 MakeTraceSourceAccessor(&NrGnbPhy::m_phySlotCtrlStats),
201 "ns3::NrGnbPhy::SlotStatsTracedCallback")
202 .AddTraceSource(
203 "RBDataStats",
204 "Resource Block used for data: SfnSf, symbol, RB PHY map, bwp ID, cell ID",
205 MakeTraceSourceAccessor(&NrGnbPhy::m_rbStatistics),
206 "ns3::NrGnbPhy::RBStatsTracedCallback");
207 return tid;
208}
209
210uint32_t
212{
213 return m_phySapUser->GetNumRbPerRbg();
214}
215
216const SfnSf&
218{
219 return m_currentSlot;
220}
221
228static uint32_t
229modulo(int n, uint32_t m)
230{
231 if (n >= 0)
232 {
233 return static_cast<uint32_t>(n) % m;
234 }
235 else
236 {
237 while (n < 0)
238 {
239 n += m;
240 }
241 return static_cast<uint32_t>(n);
242 }
243}
244
258static int32_t
259ReturnHarqSlot(const std::vector<LteNrTddSlotType>& pattern, uint32_t pos, uint32_t n1)
260{
261 int32_t k1 = static_cast<int32_t>(n1);
262
263 uint32_t index = modulo(static_cast<int>(pos) + k1, static_cast<uint32_t>(pattern.size()));
264
265 while (pattern[index] < LteNrTddSlotType::S)
266 {
267 k1++;
268 index = modulo(static_cast<int>(pos) + k1, static_cast<uint32_t>(pattern.size()));
269 NS_ASSERT(index < pattern.size());
270 }
271
272 return k1;
273}
274
275struct DciKPair
276{
277 uint32_t indexDci{0};
278 uint32_t k{0};
279};
280
289static DciKPair
290ReturnDciSlot(const std::vector<LteNrTddSlotType>& pattern, uint32_t pos, uint32_t n)
291{
292 DciKPair ret;
293 ret.k = n;
294 ret.indexDci = modulo(static_cast<int>(pos) - static_cast<int>(ret.k),
295 static_cast<uint32_t>(pattern.size()));
296
297 while (pattern[ret.indexDci] > LteNrTddSlotType::F)
298 {
299 ret.k++;
300 ret.indexDci = modulo(static_cast<int>(pos) - static_cast<int>(ret.k),
301 static_cast<uint32_t>(pattern.size()));
302 NS_ASSERT(ret.indexDci < pattern.size());
303 }
304
305 return ret;
306}
307
318static void
319GenerateDciMaps(const std::vector<LteNrTddSlotType>& pattern,
320 std::map<uint32_t, std::vector<uint32_t>>* toSend,
321 std::map<uint32_t, std::vector<uint32_t>>* generate,
322 uint32_t pos,
323 uint32_t n,
324 uint32_t l1l2CtrlLatency)
325{
326 auto dciSlot = ReturnDciSlot(pattern, pos, n);
327 uint32_t indexGen =
328 modulo(static_cast<int>(dciSlot.indexDci) - static_cast<int>(l1l2CtrlLatency),
329 static_cast<uint32_t>(pattern.size()));
330 uint32_t kWithCtrlLatency = static_cast<uint32_t>(dciSlot.k) + l1l2CtrlLatency;
331
332 (*toSend)[dciSlot.indexDci].push_back(static_cast<uint32_t>(dciSlot.k));
333 (*generate)[indexGen].push_back(kWithCtrlLatency);
334}
335
336void
337NrGnbPhy::GenerateStructuresFromPattern(const std::vector<LteNrTddSlotType>& pattern,
338 std::map<uint32_t, std::vector<uint32_t>>* toSendDl,
339 std::map<uint32_t, std::vector<uint32_t>>* toSendUl,
340 std::map<uint32_t, std::vector<uint32_t>>* generateDl,
341 std::map<uint32_t, std::vector<uint32_t>>* generateUl,
342 std::map<uint32_t, uint32_t>* dlHarqfbPosition,
343 uint32_t n0,
344 uint32_t n2,
345 uint32_t n1,
346 uint32_t l1l2CtrlLatency)
347{
348 const uint32_t n = static_cast<uint32_t>(pattern.size());
349
350 // Create a pattern that is all F.
351 std::vector<LteNrTddSlotType> fddGenerationPattern;
352 fddGenerationPattern.resize(pattern.size(), LteNrTddSlotType::F);
353
354 /* if we have to generate structs for a TDD pattern, then use the input pattern.
355 * Otherwise, pass to the gen functions a pattern which is all F (therefore, the
356 * the function will think that they will be able to transmit or
357 * receive things following n0, n1, n2, that is what happen in FDD, just in
358 * another band..
359 */
360
361 const std::vector<LteNrTddSlotType>* generationPattern;
362
363 if (IsTdd(pattern))
364 {
365 generationPattern = &pattern;
366 }
367 else
368 {
369 generationPattern = &fddGenerationPattern;
370 }
371
372 for (uint32_t i = 0; i < n; i++)
373 {
374 if ((*generationPattern)[i] == LteNrTddSlotType::UL)
375 {
376 GenerateDciMaps(*generationPattern, toSendUl, generateUl, i, n2, l1l2CtrlLatency);
377 }
378 else if ((*generationPattern)[i] == LteNrTddSlotType::DL ||
379 pattern[i] == LteNrTddSlotType::S)
380 {
381 GenerateDciMaps(*generationPattern, toSendDl, generateDl, i, n0, l1l2CtrlLatency);
382
383 int32_t k1 = ReturnHarqSlot(*generationPattern, i, n1);
384 (*dlHarqfbPosition).insert(std::make_pair(i, k1));
385 }
386 else if ((*generationPattern)[i] == LteNrTddSlotType::F)
387 {
388 GenerateDciMaps(*generationPattern, toSendDl, generateDl, i, n0, l1l2CtrlLatency);
389 GenerateDciMaps(*generationPattern, toSendUl, generateUl, i, n2, l1l2CtrlLatency);
390
391 int32_t k1 = ReturnHarqSlot(*generationPattern, i, n1);
392 (*dlHarqfbPosition).insert(std::make_pair(i, k1));
393 }
394 }
395
396 /*
397 * Now, if the input pattern is for FDD, remove the elements in the
398 * opposite generate* structures: in the end, we don't want to generate DL
399 * for a FDD-UL band, right?
400 *
401 * But.. maintain the toSend structures, as they will be used to send
402 * feedback or other messages, like DCI.
403 */
404
405 if (!IsTdd(pattern))
406 {
407 if (HasUlSlot(pattern))
408 {
409 generateDl->clear();
410 }
411 else
412 {
413 generateUl->clear();
414 }
415 }
416
417 for (auto& list : (*generateUl))
418 {
419 std::stable_sort(list.second.begin(), list.second.end());
420 }
421
422 for (auto& list : (*generateDl))
423 {
424 std::stable_sort(list.second.begin(), list.second.end());
425 }
426}
427
428void
429NrGnbPhy::PushDlAllocation(const SfnSf& sfnSf) const
430{
431 NS_LOG_FUNCTION(this);
432 NS_ASSERT(m_phySapUser);
433
434 auto dci = m_phySapUser->GetDlCtrlDci();
435 VarTtiAllocInfo dlCtrlVarTti(dci);
436
437 SlotAllocInfo slotAllocInfo = SlotAllocInfo(sfnSf);
438
439 slotAllocInfo.m_numSymAlloc = dlCtrlVarTti.m_dci->m_numSym;
440 slotAllocInfo.m_type = SlotAllocInfo::DL;
441 slotAllocInfo.m_varTtiAllocInfo.emplace_back(dlCtrlVarTti);
442
443 m_phySapProvider->SetSlotAllocInfo(slotAllocInfo);
444}
445
446void
447NrGnbPhy::PushUlAllocation(const SfnSf& sfnSf) const
448{
449 NS_LOG_FUNCTION(this);
450 NS_ASSERT(m_phySapUser);
451
452 auto dci = m_phySapUser->GetUlCtrlDci();
453 VarTtiAllocInfo ulCtrlVarTti(dci);
454
455 SlotAllocInfo slotAllocInfo = SlotAllocInfo(sfnSf);
456
457 slotAllocInfo.m_numSymAlloc = ulCtrlVarTti.m_dci->m_numSym;
458 slotAllocInfo.m_type = SlotAllocInfo::UL;
459 slotAllocInfo.m_varTtiAllocInfo.emplace_back(ulCtrlVarTti);
460
461 m_phySapProvider->SetSlotAllocInfo(slotAllocInfo);
462}
463
464void
465NrGnbPhy::SetTddPattern(const std::vector<LteNrTddSlotType>& pattern)
466{
467 NS_LOG_FUNCTION(this);
468
469 std::stringstream ss;
470
471 for (const auto& v : pattern)
472 {
473 ss << v << "|";
474 }
475 NS_LOG_INFO("Set pattern : " << ss.str());
476
477 m_tddPattern = pattern;
478
479 m_generateDl.clear();
480 m_generateUl.clear();
481 m_toSendDl.clear();
482 m_toSendUl.clear();
483 m_dlHarqfbPosition.clear();
484
485 GenerateStructuresFromPattern(pattern,
486 &m_toSendDl,
487 &m_toSendUl,
488 &m_generateDl,
489 &m_generateUl,
490 &m_dlHarqfbPosition,
491 0,
492 GetN2Delay(),
493 GetN1Delay(),
495}
496
497void
498NrGnbPhy::ScheduleStartEventLoop(uint32_t nodeId, uint16_t frame, uint8_t subframe, uint16_t slot)
499{
500 NS_LOG_FUNCTION(this);
501 Simulator::ScheduleWithContext(nodeId,
502 MilliSeconds(0),
503 &NrGnbPhy::StartEventLoop,
504 this,
505 frame,
506 subframe,
507 slot);
508}
509
510void
511NrGnbPhy::StartEventLoop(uint16_t frame, uint8_t subframe, uint16_t slot)
512{
513 NS_LOG_FUNCTION(this);
514 NS_LOG_DEBUG("PHY starting. Configuration: "
515 << std::endl
516 << "\t TxPower: " << m_txPower << " dBm" << std::endl
517 << "\t NoiseFigure: " << m_noiseFigure << std::endl
518 << "\t N0: " << m_n0Delay << std::endl
519 << "\t N1: " << m_n1Delay << std::endl
520 << "\t N2: " << m_n2Delay << std::endl
521 << "\t TbDecodeLatency: " << GetTbDecodeLatency().GetMicroSeconds() << " us "
522 << std::endl
523 << "\t Numerology: " << GetNumerology() << std::endl
524 << "\t SymbolsPerSlot: " << GetSymbolsPerSlot() << std::endl
525 << "\t Pattern: " << GetPattern() << std::endl
526 << "Attached to physical channel: " << std::endl
527 << "\t Channel bandwidth: " << GetChannelBandwidth() << " Hz" << std::endl
528 << "\t Channel central freq: " << GetCentralFrequency() << " Hz" << std::endl
529 << "\t Num. RB: " << GetRbNum());
530 SfnSf startSlot(frame, subframe, slot, GetNumerology());
532 StartSlot(startSlot);
533}
534
535void
537{
538 NS_LOG_FUNCTION(this);
539 m_gnbCphySapUser = s;
540}
541
544{
545 NS_LOG_FUNCTION(this);
546 return m_gnbCphySapProvider;
547}
548
549void
550NrGnbPhy::SetNrFhPhySapProvider(NrFhPhySapProvider* s)
551{
553}
554
555NrFhPhySapUser*
556NrGnbPhy::GetNrFhPhySapUser()
557{
558 return m_nrFhPhySapUser;
559}
560
561uint32_t
563{
564 return m_n0Delay;
565}
566
567uint32_t
569{
570 return m_n1Delay;
571}
572
573uint32_t
575{
576 return m_n2Delay;
577}
578
579void
580NrGnbPhy::SetN0Delay(uint32_t delay)
581{
582 m_n0Delay = delay;
583 SetTddPattern(m_tddPattern); // Update the generate/send structures
584}
585
586void
587NrGnbPhy::SetN1Delay(uint32_t delay)
588{
589 m_n1Delay = delay;
590 SetTddPattern(m_tddPattern); // Update the generate/send structures
591}
592
593void
594NrGnbPhy::SetN2Delay(uint32_t delay)
595{
596 m_n2Delay = delay;
597 SetTddPattern(m_tddPattern); // Update the generate/send structures
598}
599
600bool
601NrGnbPhy::DoesFhAllocationFit(uint16_t bwpId, uint32_t mcs, uint32_t nRegs, uint8_t dlRank) const
602{
603 NS_LOG_FUNCTION(this);
604 NS_ASSERT(m_nrFhPhySapProvider);
605 return m_nrFhPhySapProvider->DoesAllocationFit(bwpId, mcs, nRegs, dlRank);
606}
607
608BeamId
609NrGnbPhy::GetBeamId(uint16_t rnti) const
610{
611 NS_LOG_FUNCTION(this);
612
613 for (const auto& i : m_deviceMap)
614 {
615 Ptr<NrUeNetDevice> ueDev = DynamicCast<NrUeNetDevice>(i);
616 uint64_t ueRnti = (DynamicCast<NrUePhy>(ueDev->GetPhy(GetBwpId())))->GetRnti();
617
618 if (ueRnti == rnti && DynamicCast<UniformPlanarArray>(m_spectrumPhy->GetAntenna()))
619 {
620 NS_ASSERT(m_spectrumPhy->GetBeamManager());
621 return m_spectrumPhy->GetBeamManager()->GetBeamId(i);
622 }
623 }
624 return BeamId(0, 0);
625}
626
627void
628NrGnbPhy::SetCam(const Ptr<NrChAccessManager>& cam)
629{
630 NS_LOG_FUNCTION(this);
631 NS_ASSERT(cam != nullptr);
632 m_cam = cam;
633 m_cam->SetAccessGrantedCallback(
634 std::bind(&NrGnbPhy::ChannelAccessGranted, this, std::placeholders::_1));
635 m_cam->SetAccessDeniedCallback(std::bind(&NrGnbPhy::ChannelAccessLost, this));
636}
637
638Ptr<NrChAccessManager>
640{
641 NS_LOG_FUNCTION(this);
642 return m_cam;
643}
644
645void
647{
648 m_txPower = pow;
649}
650
651double
653{
654 return m_txPower;
655}
656
657void
658NrGnbPhy::SetSubChannels(const std::vector<int>& rbIndexVector, size_t nTotalAllocRbs)
659{
660 Ptr<SpectrumValue> txPsd = GetTxPowerSpectralDensity(rbIndexVector);
661 NS_ASSERT(txPsd);
662
663 // In case of UNIFORM_POWER_ALLOCATION_USED, the txPsd created by GetTxPowerSpectralDensity
664 // assumed that the transmit power would be split only among RBs allocated to this signal/UE.
665 // This assumption is false when there are concurrent transmissions on other RBs to other UEs
666 // (OFDMA DL). To correct this, use the combined number of used RBs to scale down txPsd.
667 if (GetPowerAllocationType() == NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED)
668 {
669 auto scaling = double(rbIndexVector.size()) / double(nTotalAllocRbs);
670 for (auto it = txPsd->ValuesBegin(); it != txPsd->ValuesEnd(); it++)
671 {
672 *it *= scaling;
673 }
674 }
675 else
676 {
677 // UNIFORM_POWER_ALLOCATION_BW: no scaling required
678 }
679
680 m_spectrumPhy->SetTxPowerSpectralDensity(txPsd);
681}
682
683void
684NrGnbPhy::QueueMib()
685{
686 NS_LOG_FUNCTION(this);
689 mib.dlBandwidth = GetChannelBandwidth() / (1000 * 100);
690 mib.systemFrameNumber = 1;
691 Ptr<NrMibMessage> mibMsg = Create<NrMibMessage>();
692 mibMsg->SetSourceBwp(GetBwpId());
693 mibMsg->SetMib(mib);
694 EnqueueCtrlMsgNow(mibMsg);
695}
696
697void
698NrGnbPhy::QueueSib()
699{
700 NS_LOG_FUNCTION(this);
701 Ptr<NrSib1Message> msg = Create<NrSib1Message>();
702 msg->SetSib1(m_sib1);
703 msg->SetSourceBwp(GetBwpId());
705}
706
707void
708NrGnbPhy::CallMacForSlotIndication(const SfnSf& currentSlot)
709{
710 NS_LOG_FUNCTION(this);
711 NS_ASSERT(!m_generateDl.empty() || !m_generateUl.empty());
712
713 m_phySapUser->SetCurrentSfn(currentSlot);
714
715 uint64_t currentSlotN = currentSlot.Normalize() % m_tddPattern.size();
716
717 NS_LOG_DEBUG("Start Slot " << currentSlot << ". In position " << currentSlotN
718 << " there is a slot of type " << m_tddPattern[currentSlotN]);
719
720 for (const auto& k2WithLatency : m_generateUl[currentSlotN])
721 {
722 SfnSf targetSlot = currentSlot;
723 targetSlot.Add(k2WithLatency);
724
725 uint64_t pos = targetSlot.Normalize() % m_tddPattern.size();
726
727 NS_LOG_DEBUG(" in slot " << currentSlot << " generate UL for " << targetSlot
728 << " which is of type " << m_tddPattern[pos]);
729
730 m_phySapUser->SlotUlIndication(targetSlot, m_tddPattern[pos]);
731 }
732
733 for (const auto& k0WithLatency : m_generateDl[currentSlotN])
734 {
735 SfnSf targetSlot = currentSlot;
736 targetSlot.Add(k0WithLatency);
737
738 uint64_t pos = targetSlot.Normalize() % m_tddPattern.size();
739
740 NS_LOG_DEBUG(" in slot " << currentSlot << " generate DL for " << targetSlot
741 << " which is of type " << m_tddPattern[pos]);
742
743 m_phySapUser->SlotDlIndication(targetSlot, m_tddPattern[pos]);
744 }
745}
746
747void
748NrGnbPhy::StartSlot(const SfnSf& startSlot)
749{
750 NS_LOG_FUNCTION(this);
751 NS_ASSERT(m_channelStatus != TO_LOSE);
752
753 m_currentSlot = startSlot;
754 m_lastSlotStart = Simulator::Now();
755
756 Simulator::Schedule(GetSlotPeriod(), &NrGnbPhy::EndSlot, this);
757
758 // update the current slot allocation; if empty (e.g., at the beginning of simu)
759 // then insert a dummy allocation, without anything.
760 if (SlotAllocInfoExists(m_currentSlot))
761 {
763 }
764 else
765 {
766 NS_LOG_WARN("No allocation for the current slot. Using an empty one");
767 m_currSlotAllocInfo = SlotAllocInfo(m_currentSlot);
768 }
769
770 if (m_isPrimary)
771 {
772 if (m_currentSlot.GetSlot() == 0)
773 {
774 bool mibOrSib = false;
775 if (m_currentSlot.GetSubframe() == 0) // send MIB at the beginning of each frame
776 {
777 QueueMib();
778 mibOrSib = true;
779 }
780 else if (m_currentSlot.GetSubframe() == 5) // send SIB at beginning of second half-frame
781 {
782 QueueSib();
783 mibOrSib = true;
784 }
786 {
787 VarTtiAllocInfo dlCtrlSlot(m_phySapUser->GetDlCtrlDci());
788 m_currSlotAllocInfo.m_varTtiAllocInfo.push_front(dlCtrlSlot);
790 }
791 }
792 }
793
794 if (m_channelStatus == GRANTED)
795 {
796 NS_LOG_INFO("Channel granted");
797 CallMacForSlotIndication(m_currentSlot);
798 DoStartSlot();
799 }
800 else
801 {
802 bool hasUlDci = false;
803 SfnSf ulSfn = m_currentSlot;
804 ulSfn.Add(GetN2Delay());
805
806 if (GetN2Delay() > 0)
807 {
808 if (SlotAllocInfoExists(ulSfn))
809 {
810 SlotAllocInfo& ulSlot = PeekSlotAllocInfo(ulSfn);
811 hasUlDci = ulSlot.ContainsDataAllocation() || ulSlot.ContainsUlCtrlAllocation() ||
812 ulSlot.ContainsUlMsg3Allocation();
813 }
814 }
815 // If there is a DL CTRL, try to obtain the channel to transmit it;
816 // because, even if right now there isn't any message, maybe they
817 // will come from another BWP.
821 {
822 // Request the channel access
823 if (m_channelStatus == NONE)
824 {
825 NS_LOG_INFO("Channel not granted, request the channel");
826 m_channelStatus = REQUESTED; // This goes always before RequestAccess()
827 m_cam->RequestAccess();
828 if (m_channelStatus == GRANTED)
829 {
830 // Repetition but we can have a CAM that gives the channel
831 // instantaneously
832 NS_LOG_INFO("Channel granted; asking MAC for SlotIndication for the future and "
833 "then start the slot");
834 CallMacForSlotIndication(m_currentSlot);
835 DoStartSlot();
836 return; // Exit without calling anything else
837 }
838 }
839 // If the channel was not granted, queue back the allocation,
840 // without calling the MAC for a new slot
841 auto slotAllocCopy = m_currSlotAllocInfo;
842 auto newSfnSf = slotAllocCopy.m_sfnSf;
843 newSfnSf.Add(1);
844 NS_LOG_INFO("Queueing allocation in front for " << newSfnSf);
846 {
847 NS_LOG_INFO("Reason: Current slot allocation has data");
848 }
849 else
850 {
851 NS_LOG_INFO("Reason: CTRL message list is not empty");
852 }
853
854 PushFrontSlotAllocInfo(newSfnSf, slotAllocCopy);
855 }
856 else
857 {
858 // It's an empty slot; ask the MAC for a new one (maybe a new data will arrive..)
859 // and just let the current one go away
860 NS_LOG_INFO("Empty slot, but asking MAC for SlotIndication for the future, maybe there "
861 "will be data");
862 CallMacForSlotIndication(m_currentSlot);
863 }
864 // If we have the UL CTRL, then schedule it (we are listening, so
865 // we don't need the channel.
866
868 {
869 for (const auto& alloc : m_currSlotAllocInfo.m_varTtiAllocInfo)
870 {
871 if (alloc.m_dci->m_type == DciInfoElementTdma::CTRL &&
872 alloc.m_dci->m_format == DciInfoElementTdma::UL)
873 {
874 Time start = GetSymbolPeriod() * alloc.m_dci->m_symStart;
875 NS_LOG_INFO("Schedule UL CTRL at " << start);
876 Simulator::Schedule(start, &NrGnbPhy::UlCtrl, this, alloc.m_dci);
877 }
878 else if (alloc.m_dci->m_type == DciInfoElementTdma::SRS &&
879 alloc.m_dci->m_format == DciInfoElementTdma::UL)
880 {
881 Time start = GetSymbolPeriod() * alloc.m_dci->m_symStart;
882 NS_LOG_INFO("Schedule UL SRS at " << start);
883 Simulator::Schedule(start, &NrGnbPhy::UlSrs, this, alloc.m_dci);
884 }
885 }
886 }
887 }
888}
889
890void
891NrGnbPhy::DoCheckOrReleaseChannel()
892{
893 NS_LOG_FUNCTION(this);
894
895 NS_ASSERT(m_channelStatus == GRANTED);
896 // The channel is granted, we have to check if we maintain it for the next
897 // slot or we have to release it.
898
899 // Assuming the scheduler assign contiguous symbol
900 uint8_t lastDlSymbol = 0;
901 for (auto& dci : m_currSlotAllocInfo.m_varTtiAllocInfo)
902 {
903 if (dci.m_dci->m_type == DciInfoElementTdma::DATA &&
904 dci.m_dci->m_format == DciInfoElementTdma::DL)
905 {
906 lastDlSymbol =
907 std::max(lastDlSymbol,
908 static_cast<uint8_t>(dci.m_dci->m_symStart + dci.m_dci->m_numSym));
909 }
910 }
911
912 Time lastDataTime = GetSymbolPeriod() * lastDlSymbol;
913
914 if (GetSlotPeriod() - lastDataTime > MicroSeconds(25))
915 {
916 NS_LOG_LOGIC("Last symbol of data: " << +lastDlSymbol
917 << ", to the end of slot we still have "
918 << (GetSlotPeriod() - lastDataTime).GetMicroSeconds()
919 << " us, so we're going to lose the channel");
920 m_channelStatus = TO_LOSE;
921 }
922 else
923 {
924 NS_LOG_LOGIC("Last symbol of data: " << +lastDlSymbol
925 << ", to the end of slot we still have "
926 << (GetSlotPeriod() - lastDataTime).GetMicroSeconds()
927 << " us, so we're NOT going to lose the channel");
928 }
929}
930
931void
932NrGnbPhy::RetrievePrepareEncodeCtrlMsgs()
933{
934 NS_LOG_FUNCTION(this);
935 auto ctrlMsgs = PopCurrentSlotCtrlMsgs();
936 ctrlMsgs.sort();
937 ctrlMsgs.merge(RetrieveMsgsFromDCIs(m_currentSlot));
938
939 if (m_netDevice != nullptr)
940 {
941 DynamicCast<NrGnbNetDevice>(m_netDevice)->RouteOutgoingCtrlMsgs(ctrlMsgs, GetBwpId());
942 }
943 else
944 {
945 // No netDevice (that could happen in tests) so just redirect them to us
946 for (const auto& msg : ctrlMsgs)
947 {
948 EncodeCtrlMsg(msg);
949 }
950 }
951}
952
953void
954NrGnbPhy::GenerateAllocationStatistics(const SlotAllocInfo& allocInfo) const
955{
956 NS_LOG_FUNCTION(this);
957 std::unordered_set<uint16_t> activeUe;
958 uint32_t availRb = GetRbNum();
959 uint32_t dataReg = 0;
960 uint32_t ctrlReg = 0;
961 uint32_t dataSym = 0;
962 uint32_t ctrlSym = 0;
963
964 int lastSymStart = -1;
965 uint32_t symUsed = 0;
966
967 for (const auto& allocation : allocInfo.m_varTtiAllocInfo)
968 {
969 uint32_t rbg = std::count(allocation.m_dci->m_rbgBitmask.begin(),
970 allocation.m_dci->m_rbgBitmask.end(),
971 1);
972
973 // First: Store the RNTI of the UE in the active list
974 if (allocation.m_dci->m_rnti != 0)
975 {
976 activeUe.insert(allocation.m_dci->m_rnti);
977 }
978
979 NS_ASSERT(lastSymStart <= allocation.m_dci->m_symStart);
980
981 auto rbgUsed = (rbg * GetNumRbPerRbg()) * allocation.m_dci->m_numSym;
982 if (allocation.m_dci->m_type == DciInfoElementTdma::DATA ||
983 allocation.m_dci->m_type == DciInfoElementTdma::MSG3)
984 {
985 dataReg += rbgUsed;
986 }
987 else
988 {
989 ctrlReg += rbgUsed;
990 }
991
992 if (lastSymStart != allocation.m_dci->m_symStart)
993 {
994 symUsed += allocation.m_dci->m_numSym;
995
996 if (allocation.m_dci->m_type == DciInfoElementTdma::DATA ||
997 allocation.m_dci->m_type == DciInfoElementTdma::MSG3)
998 {
999 dataSym += allocation.m_dci->m_numSym;
1000 }
1001 else
1002 {
1003 ctrlSym += allocation.m_dci->m_numSym;
1004 }
1005 }
1006
1007 lastSymStart = allocation.m_dci->m_symStart;
1008 }
1009
1010 NS_ASSERT_MSG(symUsed == allocInfo.m_numSymAlloc,
1011 "Allocated " << +allocInfo.m_numSymAlloc << " but only " << symUsed
1012 << " written in stats");
1013
1014 m_phySlotDataStats(allocInfo.m_sfnSf,
1015 activeUe.size(),
1016 dataReg,
1017 dataSym,
1018 availRb,
1019 GetSymbolsPerSlot() - ctrlSym,
1020 GetBwpId(),
1021 GetCellId());
1022 m_phySlotCtrlStats(allocInfo.m_sfnSf,
1023 activeUe.size(),
1024 ctrlReg,
1025 ctrlSym,
1026 availRb,
1027 GetSymbolsPerSlot() - dataSym,
1028 GetBwpId(),
1029 GetCellId());
1030}
1031
1032void
1033NrGnbPhy::DoStartSlot()
1034{
1035 NS_LOG_FUNCTION(this);
1036 NS_ASSERT(m_ctrlMsgs.empty()); // This assert has to be re-evaluated for NR-U.
1037 // We can have messages before we weren't able to tx them before.
1038
1039 uint64_t currentSlotN = m_currentSlot.Normalize() % m_tddPattern.size();
1040
1041 NS_LOG_DEBUG("Start Slot " << m_currentSlot << " of type " << m_tddPattern[currentSlotN]);
1042
1043 GenerateAllocationStatistics(m_currSlotAllocInfo);
1044
1046 {
1047 return;
1048 }
1049
1050 NS_LOG_DEBUG("Allocations of the current slot: " << std::endl << m_currSlotAllocInfo);
1051
1052 DoCheckOrReleaseChannel();
1053
1054 RetrievePrepareEncodeCtrlMsgs();
1055
1056 PrepareRbgAllocationMap(m_currSlotAllocInfo.m_varTtiAllocInfo);
1057
1058 FillTheEvent();
1059}
1060
1061void
1062NrGnbPhy::PrepareRbgAllocationMap(const std::deque<VarTtiAllocInfo>& allocations)
1063{
1064 NS_LOG_FUNCTION(this);
1065
1066 // Start with a clean RBG allocation bitmask
1067 m_rbgAllocationPerSym.clear();
1068
1069 // Create RBG map to know where to put power in DL
1070 for (const auto& allocation : allocations)
1071 {
1072 if (allocation.m_dci->m_type != DciInfoElementTdma::CTRL)
1073 {
1074 if (allocation.m_dci->m_format == DciInfoElementTdma::DL)
1075 {
1076 // In m_rbgAllocationPerSym, store only the DL RBG set to 1:
1077 // these will used to put power
1078 StoreRBGAllocation(&m_rbgAllocationPerSym, allocation.m_dci);
1079 }
1080
1081 // For statistics, store UL/DL allocations
1082 StoreRBGAllocation(&m_rbgAllocationPerSymDataStat, allocation.m_dci);
1083 }
1084 }
1085
1086 for (const auto& s : m_rbgAllocationPerSymDataStat)
1087 {
1088 auto& rbgAllocation = s.second;
1089 m_rbStatistics(m_currentSlot,
1090 s.first,
1091 FromRBGBitmaskToRBAssignment(rbgAllocation),
1092 GetBwpId(),
1093 GetCellId());
1094 }
1095
1096 m_rbgAllocationPerSymDataStat.clear();
1097}
1098
1099void
1100NrGnbPhy::FillTheEvent()
1101{
1102 NS_LOG_FUNCTION(this);
1103
1104 uint8_t lastSymStart = 0;
1105 for (const auto& allocation : m_currSlotAllocInfo.m_varTtiAllocInfo)
1106 {
1107 NS_ASSERT(lastSymStart <= allocation.m_dci->m_symStart);
1108
1109 auto varTtiStart = GetSymbolPeriod() * allocation.m_dci->m_symStart;
1110 Simulator::Schedule(varTtiStart, &NrGnbPhy::StartVarTti, this, allocation.m_dci);
1111 lastSymStart = allocation.m_dci->m_symStart;
1112
1113 NS_LOG_INFO("Scheduled allocation " << *(allocation.m_dci) << " at " << varTtiStart);
1114 }
1115
1117}
1118
1119void
1120NrGnbPhy::StoreRBGAllocation(std::unordered_map<uint8_t, std::vector<bool>>* map,
1121 const std::shared_ptr<DciInfoElementTdma>& dci) const
1122{
1123 NS_LOG_FUNCTION(this);
1124
1125 auto itAlloc = map->find(dci->m_symStart);
1126 if (itAlloc == map->end())
1127 {
1128 itAlloc = map->insert(std::make_pair(dci->m_symStart, dci->m_rbgBitmask)).first;
1129 }
1130 else
1131 {
1132 auto& existingRBGBitmask = itAlloc->second;
1133 NS_ASSERT(existingRBGBitmask.size() == dci->m_rbgBitmask.size());
1134 for (uint32_t i = 0; i < existingRBGBitmask.size(); ++i)
1135 {
1136 existingRBGBitmask.at(i) = existingRBGBitmask.at(i) || dci->m_rbgBitmask.at(i);
1137 }
1138 }
1139}
1140
1141std::list<Ptr<NrControlMessage>>
1142NrGnbPhy::RetrieveDciFromAllocation(const SlotAllocInfo& alloc,
1143 const DciInfoElementTdma::DciFormat& format,
1144 uint32_t kDelay,
1145 uint32_t k1Delay)
1146{
1147 NS_LOG_FUNCTION(this);
1148 std::list<Ptr<NrControlMessage>> ctrlMsgs;
1149
1150 if (!alloc.m_buildRarList.empty())
1151 {
1152 Ptr<NrRarMessage> ulMsg3DciMsg = Create<NrRarMessage>();
1153 for (const auto& rarIt : alloc.m_buildRarList)
1154 {
1155 NrRarMessage::Rar rar{};
1156 // RA preamble and RNTI should be set before by MAC/scheduler
1157 NS_ASSERT(rarIt.raPreambleId != 255);
1158 rar.rarPayload = rarIt;
1159 rar.rarPayload.k2Delay = kDelay;
1160 ulMsg3DciMsg->AddRar(rar);
1161
1162 NS_LOG_INFO("In slot " << m_currentSlot << " PHY retrieves the RAR message for RNTI "
1163 << rar.rarPayload.ulMsg3Dci->m_rnti << " RA preamble Id "
1164 << +rar.rarPayload.raPreambleId << " at:" << Simulator::Now()
1165 << " for slot:" << alloc.m_sfnSf << " kDelay:" << kDelay
1166 << "k1Delay:" << k1Delay);
1167 ulMsg3DciMsg->SetSourceBwp(GetBwpId());
1168 }
1169 if (kDelay != 0)
1170 {
1171 ctrlMsgs.push_back(ulMsg3DciMsg);
1172 }
1173 }
1174
1175 for (const auto& dlAlloc : alloc.m_varTtiAllocInfo)
1176 {
1177 if (dlAlloc.m_dci->m_type != DciInfoElementTdma::CTRL &&
1178 dlAlloc.m_dci->m_type != DciInfoElementTdma::MSG3 // we are sending MSG3 grant via RAR
1179 // message, we cannot also send UL DCI
1180 && dlAlloc.m_dci->m_format == format)
1181 {
1182 auto& dciElem = dlAlloc.m_dci;
1183 NS_ASSERT(dciElem->m_format == format);
1184 NS_ASSERT_MSG(dciElem->m_symStart + dciElem->m_numSym <= GetSymbolsPerSlot(),
1185 "symStart: " << static_cast<uint32_t>(dciElem->m_symStart)
1186 << " numSym: " << static_cast<uint32_t>(dciElem->m_numSym)
1187 << " symPerSlot: "
1188 << static_cast<uint32_t>(GetSymbolsPerSlot()));
1189
1190 NS_LOG_INFO("Send DCI to RNTI " << dciElem->m_rnti << " from sym "
1191 << +dciElem->m_symStart << " to "
1192 << +dciElem->m_symStart + dciElem->m_numSym);
1193
1194 Ptr<NrControlMessage> msg;
1195
1196 if (dciElem->m_format == DciInfoElementTdma::DL)
1197 {
1198 Ptr<NrDlDciMessage> dciMsg = Create<NrDlDciMessage>(dciElem);
1199
1200 dciMsg->SetSourceBwp(GetBwpId());
1201 dciMsg->SetKDelay(kDelay);
1202 dciMsg->SetK1Delay(k1Delay);
1203 msg = dciMsg;
1204 }
1205 else
1206 {
1207 Ptr<NrUlDciMessage> dciMsg = Create<NrUlDciMessage>(dciElem);
1208
1209 dciMsg->SetSourceBwp(GetBwpId());
1210 dciMsg->SetKDelay(kDelay);
1211 msg = dciMsg;
1212 }
1213
1214 ctrlMsgs.push_back(msg);
1215 }
1216 }
1217 ctrlMsgs.sort();
1218 return ctrlMsgs;
1219}
1220
1221std::list<Ptr<NrControlMessage>>
1222NrGnbPhy::RetrieveMsgsFromDCIs(const SfnSf& currentSlot)
1223{
1224 std::list<Ptr<NrControlMessage>> ctrlMsgs;
1225 uint64_t currentSlotN = currentSlot.Normalize() % m_tddPattern.size();
1226
1227 uint32_t k1delay = m_dlHarqfbPosition[currentSlotN];
1228
1229 // TODO: copy paste :(
1230 for (const auto& k0delay : m_toSendDl[currentSlotN])
1231 {
1232 SfnSf targetSlot = currentSlot;
1233
1234 targetSlot.Add(k0delay);
1235
1236 if (targetSlot == currentSlot)
1237 {
1238 NS_LOG_DEBUG(" in slot " << currentSlot << " send DL DCI for the same slot");
1239
1240 ctrlMsgs.merge(RetrieveDciFromAllocation(m_currSlotAllocInfo,
1242 k0delay,
1243 k1delay));
1244 }
1245 else if (SlotAllocInfoExists(targetSlot))
1246 {
1247 NS_LOG_DEBUG(" in slot " << currentSlot << " send DL DCI for " << targetSlot);
1248
1249 ctrlMsgs.merge(RetrieveDciFromAllocation(PeekSlotAllocInfo(targetSlot),
1251 k0delay,
1252 k1delay));
1253 }
1254 else
1255 {
1256 NS_LOG_DEBUG("No allocation found for slot " << targetSlot);
1257 }
1258 }
1259
1260 for (const auto& k2delay : m_toSendUl[currentSlotN])
1261 {
1262 SfnSf targetSlot = currentSlot;
1263
1264 targetSlot.Add(k2delay);
1265
1266 if (targetSlot == currentSlot)
1267 {
1268 NS_LOG_DEBUG(" in slot " << currentSlot << " send UL DCI for the same slot");
1269
1270 ctrlMsgs.merge(RetrieveDciFromAllocation(m_currSlotAllocInfo,
1272 k2delay,
1273 k1delay));
1274 }
1275 else if (SlotAllocInfoExists(targetSlot))
1276 {
1277 NS_LOG_DEBUG(" in slot " << currentSlot << " send UL DCI for " << targetSlot);
1278
1279 ctrlMsgs.merge(RetrieveDciFromAllocation(PeekSlotAllocInfo(targetSlot),
1281 k2delay,
1282 k1delay));
1283 }
1284 else
1285 {
1286 NS_LOG_DEBUG("No allocation found for slot " << targetSlot);
1287 }
1288 }
1289 ctrlMsgs.sort();
1290 return ctrlMsgs;
1291}
1292
1293Time
1294NrGnbPhy::DlCtrl(const std::shared_ptr<DciInfoElementTdma>& dci)
1295{
1296 NS_LOG_FUNCTION(this);
1297
1298 NS_LOG_DEBUG("Starting DL CTRL TTI at symbol " << +m_currSymStart << " to "
1299 << +m_currSymStart + dci->m_numSym);
1300
1301 // TX control period
1302 Time varTtiPeriod = GetSymbolPeriod() * dci->m_numSym;
1303
1304 bool transmitCsiRs = false;
1305 if (m_enableCsiRs)
1306 {
1307 // Check whether it is time to transmit CSI-RS
1308 uint16_t currentCsiRsOffset = m_currentSlot.Normalize() % m_csiRsPeriodicity;
1309 if (TimeToTransmitCsiRs(currentCsiRsOffset))
1310 {
1311 varTtiPeriod = ScheduleCsiRs(varTtiPeriod, currentCsiRsOffset);
1312 transmitCsiRs = true;
1313 }
1314 }
1315
1316 // The function that is filling m_ctrlMsgs is NrPhy::encodeCtrlMsgs
1317 if (!m_ctrlMsgs.empty() || transmitCsiRs)
1318 {
1319 NS_LOG_DEBUG("gNB TXing DL CTRL with "
1320 << m_ctrlMsgs.size() << " msgs, frame " << m_currentSlot << " symbols "
1321 << static_cast<uint32_t>(dci->m_symStart) << "-"
1322 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym - 1) << " start "
1323 << Simulator::Now() << " end "
1324 << Simulator::Now() + varTtiPeriod - NanoSeconds(1.0));
1325 for (auto& m_ctrlMsg : m_ctrlMsgs)
1326 {
1327 Ptr<NrControlMessage> msg = m_ctrlMsg;
1328 m_phyTxedCtrlMsgsTrace(m_currentSlot, GetCellId(), dci->m_rnti, GetBwpId(), msg);
1329 }
1330
1331 SendCtrlChannels(varTtiPeriod -
1332 NanoSeconds(1.0)); // -1 ns ensures control ends before data period
1333 }
1334 else
1335 {
1336 NS_LOG_DEBUG("No messages to send, skipping");
1337 }
1338
1339 return varTtiPeriod;
1340}
1341
1342bool
1343NrGnbPhy::TimeToTransmitCsiRs(uint16_t currentOffset) const
1344{
1345 if (!m_csiRsOffsetToUes.contains(currentOffset))
1346 {
1347 return false;
1348 }
1349 else
1350 {
1351 return !m_csiRsOffsetToUes.at(currentOffset).empty();
1352 }
1353}
1354
1355void
1356NrGnbPhy::TransmitCsiRsPerUe(Ptr<NrUeNetDevice> ueDev)
1357{
1358 NS_LOG_FUNCTION(this);
1360 uint64_t rnti = (DynamicCast<NrUePhy>(ueDev->GetPhy(GetBwpId())))->GetRnti();
1361
1362 NS_LOG_DEBUG("Transmitting CSI-RS towards UE with IMSI : " << ueDev->GetImsi() << " at slot:"
1363 << +m_currentSlot.Normalize());
1364 m_spectrumPhy->StartTxCsiRs(rnti, 0);
1365}
1366
1367Time
1368NrGnbPhy::ScheduleCsiRs(Time ctrlVarTti, uint16_t currentOffset)
1369{
1370 NS_ASSERT_MSG(!m_spectrumPhy->IsTransmitting(),
1371 "Should have finished transmission of CTRL already.");
1372
1373 if (m_csiRsModel == CSI_RS_PER_UE)
1374 {
1375 // CSI-RS is the duration of 1 nanosecond plus we want a
1376 // 1 nanosecond pause between the independent CSI-RS transmissions
1377 ctrlVarTti -= m_deviceMap.size() * NanoSeconds(2);
1378
1379 uint16_t ueCounter = 0;
1380 for (auto& i : m_csiRsOffsetToUes.at(currentOffset))
1381 {
1382 Ptr<NrUeNetDevice> ueDev = DynamicCast<NrUeNetDevice>(i);
1383 Simulator::Schedule(ctrlVarTti + NanoSeconds(2.0) * ueCounter,
1384 &NrGnbPhy::TransmitCsiRsPerUe,
1385 this,
1386 ueDev);
1387
1388 ueCounter++;
1389 }
1390 }
1391 return ctrlVarTti;
1392}
1393
1394Time
1395NrGnbPhy::UlCtrl(const std::shared_ptr<DciInfoElementTdma>& dci)
1396{
1397 NS_LOG_FUNCTION(this);
1398
1399 NS_LOG_DEBUG("Starting UL CTRL TTI at symbol " << +m_currSymStart << " to "
1400 << +m_currSymStart + dci->m_numSym);
1401
1402 Time varTtiPeriod = GetSymbolPeriod() * dci->m_numSym;
1403
1404 NS_LOG_DEBUG("gNB RXng UL CTRL frame "
1405 << m_currentSlot << " symbols " << static_cast<uint32_t>(dci->m_symStart) << "-"
1406 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym - 1) << " start "
1407 << Simulator::Now() << " end " << Simulator::Now() + varTtiPeriod);
1408 return varTtiPeriod;
1409}
1410
1411Time
1412NrGnbPhy::DlData(const std::shared_ptr<DciInfoElementTdma>& dci)
1413{
1414 NS_LOG_FUNCTION(this);
1415 NS_LOG_DEBUG("Starting DL DATA TTI at symbol " << +m_currSymStart << " to "
1416 << +m_currSymStart + dci->m_numSym << " for "
1417 << +dci->m_rnti);
1418
1419 Time varTtiPeriod = GetSymbolPeriod() * dci->m_numSym;
1420
1421 Ptr<PacketBurst> pktBurst = GetPacketBurst(m_currentSlot, dci->m_symStart, dci->m_rnti);
1422
1423 if (!pktBurst || pktBurst->GetNPackets() == 0)
1424 {
1425 // sometimes the UE will be scheduled when no data is queued.
1426 // In this case, don't send anything, don't put power... do nothing!
1427 return varTtiPeriod;
1428 }
1429
1430 NS_LOG_INFO("gNB TXing DL DATA frame "
1431 << m_currentSlot << " symbols " << static_cast<uint32_t>(dci->m_symStart) << "-"
1432 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym - 1) << " start "
1433 << Simulator::Now() + NanoSeconds(1) << " end "
1434 << Simulator::Now() + varTtiPeriod - NanoSeconds(2.0));
1435
1436 Simulator::Schedule(NanoSeconds(1.0),
1437 &NrGnbPhy::SendDataChannels,
1438 this,
1439 pktBurst,
1440 varTtiPeriod - NanoSeconds(2.0),
1441 dci);
1442
1443 return varTtiPeriod;
1444}
1445
1446Time
1447NrGnbPhy::UlData(const std::shared_ptr<DciInfoElementTdma>& dci)
1448{
1449 NS_LOG_FUNCTION(this);
1450
1451 NS_LOG_DEBUG("Starting UL DATA TTI at symbol " << +m_currSymStart << " to "
1452 << +m_currSymStart + dci->m_numSym);
1453
1454 Time varTtiPeriod = GetSymbolPeriod() * dci->m_numSym;
1455
1456 m_spectrumPhy->AddExpectedTb({dci->m_ndi,
1457 dci->m_tbSize,
1458 dci->m_mcs,
1459 dci->m_rank,
1460 dci->m_rnti,
1461 FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask),
1462 dci->m_harqProcess,
1463 dci->m_rv,
1464 false,
1465 dci->m_symStart,
1466 dci->m_numSym,
1467 m_currentSlot});
1468
1469 bool found = false;
1470 for (auto& i : m_deviceMap)
1471 {
1472 Ptr<NrUeNetDevice> ueDev = DynamicCast<NrUeNetDevice>(i);
1473 uint64_t ueRnti = (DynamicCast<NrUePhy>(ueDev->GetPhy(GetBwpId())))->GetRnti();
1474 if (dci->m_rnti == ueRnti)
1475 {
1476 // Even if we change the beamforming vector, we hope that the scheduler
1477 // has scheduled UEs within the same beam (and, therefore, have the same
1478 // beamforming vector)
1479 // Beamforming vector should be available only when the node has a UPA antenna device
1480 if (DynamicCast<UniformPlanarArray>(m_spectrumPhy->GetAntenna()))
1481 {
1482 ChangeBeamformingVector(i); // assume the control signal is omni
1483 }
1484 found = true;
1485 break;
1486 }
1487 }
1488 // In case UE was not attached via NrHelper::AttachToGnb(),
1489 // assume quasi omni beamforming until we have the opportunity to scan for a beam
1490 if (!found)
1491 {
1492 ChangeBeamformingVector(nullptr);
1493 }
1494
1495 NS_LOG_INFO("GNB RXing UL DATA frame "
1496 << m_currentSlot << " symbols " << static_cast<uint32_t>(dci->m_symStart) << "-"
1497 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym - 1) << " start "
1498 << Simulator::Now() << " end " << Simulator::Now() + varTtiPeriod);
1499 return varTtiPeriod;
1500}
1501
1502void
1504{
1505 m_spectrumPhy->GetBeamManager()->ChangeBeamformingVector(dev);
1506}
1507
1508void
1510{
1511 m_spectrumPhy->GetBeamManager()->ChangeToQuasiOmniBeamformingVector();
1512}
1513
1514Time
1515NrGnbPhy::UlSrs(const std::shared_ptr<DciInfoElementTdma>& dci)
1516{
1517 NS_LOG_FUNCTION(this);
1518
1519 NS_LOG_DEBUG("Starting UL SRS TTI at symbol " << +m_currSymStart << " to "
1520 << +m_currSymStart + dci->m_numSym);
1521
1522 Time varTtiPeriod = GetSymbolPeriod() * dci->m_numSym;
1523
1524 m_spectrumPhy->AddExpectedSrsRnti(dci->m_rnti);
1525
1526 bool found = false;
1527
1528 // if yes, and the rnti for the current SRS is not found in the list,
1529 // the code will not abort
1530 for (auto& i : m_deviceMap)
1531 {
1532 Ptr<NrUeNetDevice> ueDev = DynamicCast<NrUeNetDevice>(i);
1533 uint64_t ueRnti = (DynamicCast<NrUePhy>(ueDev->GetPhy(0)))->GetRnti();
1534 if (dci->m_rnti == ueRnti)
1535 {
1536 // Even if we change the beamforming vector, we hope that the scheduler
1537 // has scheduled UEs within the same beam (and, therefore, have the same
1538 // beamforming vector)
1539 // Beamforming vector should be available only when the node has a UPA antenna device
1540 if (DynamicCast<UniformPlanarArray>(m_spectrumPhy->GetAntenna()))
1541 {
1542 ChangeBeamformingVector(i); // assume the control signal is omni
1543 }
1544 found = true;
1545 break;
1546 }
1547 }
1548
1549 // In case UE was not attached via NrHelper::AttachToGnb(),
1550 // assume quasi omni beamforming until we have the opportunity to scan for a beam
1551 if (!found)
1552 {
1553 ChangeBeamformingVector(nullptr);
1554 NS_LOG_WARN("The UE for which is scheduled this SRS does not have yet initialized RNTI. "
1555 "RAR message was not received yet.");
1556 }
1557
1558 NS_LOG_INFO("GNB RXing UL SRS frame "
1559 << m_currentSlot << " symbols " << static_cast<uint32_t>(dci->m_symStart) << "-"
1560 << static_cast<uint32_t>(dci->m_symStart + dci->m_numSym - 1) << " start "
1561 << Simulator::Now() << " end " << Simulator::Now() + varTtiPeriod);
1562 return varTtiPeriod;
1563}
1564
1565void
1566NrGnbPhy::StartVarTti(const std::shared_ptr<DciInfoElementTdma>& dci)
1567{
1568 NS_LOG_FUNCTION(this);
1569 if (DynamicCast<UniformPlanarArray>(m_spectrumPhy->GetAntenna()))
1570 {
1571 ChangeToQuasiOmniBeamformingVector(); // assume the control signal is omni
1572 }
1573 m_currSymStart = dci->m_symStart;
1574
1575 Time varTtiPeriod;
1576
1577 if (dci->m_type == DciInfoElementTdma::CTRL)
1578 {
1579 if (dci->m_format == DciInfoElementTdma::DL)
1580 {
1581 varTtiPeriod = DlCtrl(dci);
1582 }
1583 else if (dci->m_format == DciInfoElementTdma::UL)
1584 {
1585 varTtiPeriod = UlCtrl(dci);
1586 }
1587 }
1588 else if (dci->m_type == DciInfoElementTdma::DATA || dci->m_type == DciInfoElementTdma::MSG3)
1589 {
1590 if (dci->m_format == DciInfoElementTdma::DL)
1591 {
1592 varTtiPeriod = DlData(dci);
1593 }
1594 else if (dci->m_format == DciInfoElementTdma::UL)
1595 {
1596 varTtiPeriod = UlData(dci);
1597 }
1598 }
1599 else if (dci->m_type == DciInfoElementTdma::SRS)
1600 {
1601 NS_ASSERT(dci->m_format == DciInfoElementTdma::UL);
1602 varTtiPeriod = UlSrs(dci);
1603 }
1604
1605 Simulator::Schedule(varTtiPeriod, &NrGnbPhy::EndVarTti, this, dci);
1606}
1607
1608void
1609NrGnbPhy::EndVarTti(const std::shared_ptr<DciInfoElementTdma>& lastDci)
1610{
1611 NS_LOG_FUNCTION(this << Simulator::Now().GetSeconds());
1612
1613 NS_LOG_DEBUG("DCI started at symbol "
1614 << static_cast<uint32_t>(lastDci->m_symStart) << " which lasted for "
1615 << static_cast<uint32_t>(lastDci->m_numSym) << " symbols finished");
1616}
1617
1618void
1619NrGnbPhy::EndSlot()
1620{
1621 NS_LOG_FUNCTION(this);
1622
1623 Time slotStart = m_lastSlotStart + GetSlotPeriod() - Simulator::Now();
1624
1625 if (m_channelStatus == TO_LOSE)
1626 {
1627 NS_LOG_INFO("Release the channel because we did not have any data to maintain the grant");
1628 m_channelStatus = NONE;
1629 m_channelLostTimer.Cancel();
1630 }
1631
1632 NS_LOG_DEBUG("Slot started at " << m_lastSlotStart << " ended");
1633
1635 {
1636 NS_LOG_DEBUG("End slot notified from PHY"); // TODO: Add active UEs nad BWPs?
1637 m_nrFhPhySapProvider->NotifyEndSlot(GetBwpId(), m_currentSlot);
1638 }
1639
1640 m_currentSlot.Add(1);
1641 Simulator::Schedule(slotStart, &NrGnbPhy::StartSlot, this, m_currentSlot);
1642}
1643
1644void
1645NrGnbPhy::SendDataChannels(const Ptr<PacketBurst>& pb,
1646 const Time& varTtiPeriod,
1647 const std::shared_ptr<DciInfoElementTdma>& dci)
1648{
1649 NS_LOG_FUNCTION(this);
1650 // update beamforming vectors (currently supports 1 user only)
1651
1652 // In each time instance, there can only be a single BF vector. Only update BF vectors once
1653 // unless time has changed
1654 if (Simulator::Now() > m_lastBfChange)
1655 {
1656 NS_ASSERT_MSG(!m_spectrumPhy->IsTransmitting(),
1657 "Cannot change analog BF after TX has started");
1658 m_lastBfChange = Simulator::Now();
1659 bool found = false;
1660 for (auto& i : m_deviceMap)
1661 {
1662 Ptr<NrUeNetDevice> ueDev = DynamicCast<NrUeNetDevice>(i);
1663 uint64_t ueRnti = (DynamicCast<NrUePhy>(ueDev->GetPhy(GetBwpId())))->GetRnti();
1664 if (dci->m_rnti == ueRnti)
1665 {
1666 if (DynamicCast<UniformPlanarArray>(m_spectrumPhy->GetAntenna()))
1667 {
1669 }
1670
1671 found = true;
1672 break;
1673 }
1674 }
1675 // In case UE was not attached via NrHelper::AttachToGnb(),
1676 // assume quasi omni beamforming until we have the opportunity to scan for a beam
1677 if (!found)
1678 {
1679 ChangeBeamformingVector(nullptr);
1680 }
1681 }
1682
1683 // in the map we stored the RBG allocated by the MAC for this symbol.
1684 // If the transmission last n symbol (n > 1 && n < 12) the SetSubChannels
1685 // doesn't need to be called again. In fact, SendDataChannels will be
1686 // invoked only when the symStart changes.
1687 NS_ASSERT(m_rbgAllocationPerSym.find(dci->m_symStart) != m_rbgAllocationPerSym.end());
1688 auto nTotalAllocRbs =
1689 FromRBGBitmaskToRBAssignment(m_rbgAllocationPerSym.at(dci->m_symStart)).size();
1690 SetSubChannels(FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask), nTotalAllocRbs);
1691
1692 std::list<Ptr<NrControlMessage>> ctrlMsgs;
1693 m_spectrumPhy->StartTxDataFrames(pb, ctrlMsgs, dci, varTtiPeriod);
1694}
1695
1696void
1697NrGnbPhy::SendCtrlChannels(const Time& varTtiPeriod)
1698{
1699 NS_LOG_FUNCTION(this << "Send Ctrl");
1700
1701 std::vector<int> fullBwRb(GetRbNum());
1702 // The first time set the right values for the phy
1703 for (uint32_t i = 0; i < fullBwRb.size(); ++i)
1704 {
1705 fullBwRb[i] = static_cast<int>(i);
1706 }
1707
1708 // Transmit power for the current signal is distributed over the full bandwidth. This is the
1709 // only signal, so the bandwidth occupied by all concurrent transmissions is also the full
1710 // bandwidth.
1711 SetSubChannels(fullBwRb, fullBwRb.size());
1712
1715 {
1716 std::vector<Ptr<NrControlMessage>> fhCtrlMsgs(m_ctrlMsgs.begin(), m_ctrlMsgs.end());
1717 auto rng = std::default_random_engine{};
1718 std::shuffle(std::begin(fhCtrlMsgs), std::end(fhCtrlMsgs), rng);
1719
1720 for (auto ctrlIt = fhCtrlMsgs.begin(); ctrlIt != fhCtrlMsgs.end(); /* no incr */)
1721 {
1722 Ptr<NrControlMessage> msg = (*ctrlIt);
1723 if (msg->GetMessageType() == NrControlMessage::DL_DCI)
1724 {
1725 auto dciMsg = DynamicCast<NrDlDciMessage>(msg);
1726 auto dciInfoElem = dciMsg->GetDciInfoElement();
1727 long rbgAssigned = std::count(dciInfoElem->m_rbgBitmask.begin(),
1728 dciInfoElem->m_rbgBitmask.end(),
1729 1);
1730
1732 dciInfoElem->m_mcs,
1733 rbgAssigned * dciInfoElem->m_numSym,
1734 dciInfoElem->m_rank) == 0)
1735 {
1736 // drop DL DCI because data does not fit in available FH BW
1737 ctrlIt = fhCtrlMsgs.erase(ctrlIt);
1738 m_ctrlMsgs.remove(msg);
1739 }
1740 else
1741 {
1742 ++ctrlIt;
1743 m_nrFhPhySapProvider->UpdateTracesBasedOnDroppedData(GetBwpId(),
1744 dciInfoElem->m_mcs,
1745 rbgAssigned,
1746 dciInfoElem->m_numSym,
1747 dciInfoElem->m_rank);
1748 }
1749 }
1750 else
1751 {
1752 ++ctrlIt;
1753 }
1754 }
1755 if (!m_ctrlMsgs.empty())
1756 {
1757 m_spectrumPhy->StartTxDlControlFrames(m_ctrlMsgs, varTtiPeriod);
1758 }
1759 m_ctrlMsgs.clear();
1760 }
1761 else
1762 {
1763 m_spectrumPhy->StartTxDlControlFrames(m_ctrlMsgs, varTtiPeriod);
1764 m_ctrlMsgs.clear();
1765 }
1766}
1767
1768void
1769NrGnbPhy::AssignCsiRsOffset(const Ptr<NrUeNetDevice>& ueDevice)
1770{
1771 NS_LOG_FUNCTION(this);
1772
1773 if (m_csiRsOffsetToUes.empty())
1774 {
1775 NS_ABORT_MSG_UNLESS(m_csiRsPeriodicity % m_tddPattern.size() == 0,
1776 "CSI-RS periodicity should be a multiply of TDD pattern size");
1777 // how many patterns falls into the CSI periodicity
1778 uint8_t repetitions = m_csiRsPeriodicity / m_tddPattern.size();
1779
1780 for (uint8_t round = 0; round < repetitions; ++round)
1781 {
1782 // count available slots for the CSI-RS
1783 for (size_t index = 0; index < m_tddPattern.size(); index++)
1784 {
1785 if (m_tddPattern[index] != LteNrTddSlotType::UL)
1786 {
1787 m_csiRsOffsetToUes[m_tddPattern.size() * round + index] =
1788 std::set<Ptr<NrUeNetDevice>>();
1789 }
1790 }
1791 }
1792 }
1793
1794 size_t lastAssignedOffset = m_csiRsOffsetToUes.begin()->second.size();
1795
1796 // searching for the next available offset value
1797 for (auto& i : m_csiRsOffsetToUes)
1798 {
1799 if (i.second.size() < lastAssignedOffset)
1800 {
1801 i.second.emplace(ueDevice);
1802 NS_LOG_DEBUG("Assigning CSI-RS offset for UE with IMSI: " << ueDevice->GetImsi());
1803 return;
1804 }
1805 lastAssignedOffset = i.second.size();
1806 }
1807 // we are here because all the offset have the same number of users assigned so
1808 // the new user starts from the first offset value
1809 NS_LOG_DEBUG("Assigning CSI-RS offset for UE with IMSI: " << ueDevice->GetImsi());
1810 m_csiRsOffsetToUes.begin()->second.emplace(ueDevice);
1811}
1812
1813bool
1814NrGnbPhy::RegisterUe(uint64_t imsi, const Ptr<NrUeNetDevice>& ueDevice)
1815{
1816 NS_LOG_FUNCTION(this << imsi);
1817 std::set<uint64_t>::iterator it;
1818 it = m_ueAttached.find(imsi);
1819
1820 if (it == m_ueAttached.end())
1821 {
1822 m_ueAttached.insert(imsi);
1823 m_deviceMap.push_back(ueDevice);
1824
1825 if (m_enableCsiRs && HasDlSlot(m_tddPattern))
1826 {
1827 AssignCsiRsOffset(ueDevice);
1828 }
1829 return (true);
1830 }
1831 else
1832 {
1833 NS_LOG_ERROR("Programming error...UE already attached");
1834 return (false);
1835 }
1836}
1837
1838void
1840{
1841 Simulator::ScheduleWithContext(m_netDevice->GetNode()->GetId(),
1844 m_phySapUser,
1845 p);
1846}
1847
1848void
1849NrGnbPhy::GenerateDataCqiReport(const SpectrumValue& sinr)
1850{
1851 NS_LOG_FUNCTION(this << sinr);
1852
1853 Values::const_iterator it;
1855 ulcqi.m_ulCqi.m_type = UlCqiInfo::PUSCH;
1856 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
1857 {
1858 // double sinrdb = 10 * std::log10 ((*it));
1859 // NS_LOG_INFO ("ULCQI RB " << i << " value " << sinrdb);
1860 // convert from double to fixed point notaltion Sxxxxxxxxxxx.xxx
1861 // int16_t sinrFp = nr::FfConverter::double2fpS11dot3 (sinrdb);
1862 ulcqi.m_ulCqi.m_sinr.push_back(
1863 *it); // will be processed by NrMacSchedulerCQIManagement::UlSBCQIReported, it will
1864 // look into a map of assignment
1865 }
1866
1867 // here we use the start symbol index of the var tti in place of the var tti index because
1868 // the absolute UL var tti index is not known to the scheduler when m_allocationMap gets
1869 // populated
1870 ulcqi.m_sfnSf = m_currentSlot;
1871 ulcqi.m_symStart = m_currSymStart;
1872 SpectrumValue newSinr = sinr;
1873 m_ulSinrTrace(0, newSinr, newSinr);
1874 m_phySapUser->UlCqiReport(ulcqi);
1875}
1876
1877void
1878NrGnbPhy::PhyCtrlMessagesReceived(const Ptr<NrControlMessage>& msg)
1879{
1880 NS_LOG_FUNCTION(this);
1881
1882 if (msg->GetMessageType() == NrControlMessage::DL_CQI)
1883 {
1884 Ptr<NrDlCqiMessage> dlcqi = DynamicCast<NrDlCqiMessage>(msg);
1885 DlCqiInfo dlcqiLE = dlcqi->GetDlCqi();
1886 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), dlcqiLE.m_rnti, GetBwpId(), msg);
1887
1888 NS_LOG_INFO("Received DL_CQI for RNTI: " << dlcqiLE.m_rnti << " in slot " << m_currentSlot);
1889
1890 m_phySapUser->ReceiveControlMessage(msg);
1891 }
1892 else if (msg->GetMessageType() == NrControlMessage::RACH_PREAMBLE)
1893 {
1894 NS_LOG_INFO("received RACH_PREAMBLE");
1895
1896 Ptr<NrRachPreambleMessage> rachPreamble = DynamicCast<NrRachPreambleMessage>(msg);
1897 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), 0, GetBwpId(), msg);
1898 NS_LOG_INFO("Received RACH Preamble in slot " << m_currentSlot);
1899 m_phySapUser->ReceiveRachPreamble(rachPreamble->GetRapId());
1900 }
1901 else if (msg->GetMessageType() == NrControlMessage::DL_HARQ)
1902 {
1903 Ptr<NrDlHarqFeedbackMessage> dlharqMsg = DynamicCast<NrDlHarqFeedbackMessage>(msg);
1904 DlHarqInfo dlharq = dlharqMsg->GetDlHarqFeedback();
1905 if (m_ueAttachedRnti.find(dlharq.m_rnti) != m_ueAttachedRnti.end())
1906 {
1907 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), dlharq.m_rnti, GetBwpId(), msg);
1908
1909 NS_LOG_INFO("Received DL_HARQ for RNTI: " << dlharq.m_rnti << " in slot "
1910 << m_currentSlot);
1911 m_phySapUser->ReceiveControlMessage(msg);
1912 }
1913 }
1914 else
1915 {
1916 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), 0, GetBwpId(), msg);
1917 m_phySapUser->ReceiveControlMessage(msg);
1918 }
1919}
1920
1924
1925void
1926NrGnbPhy::DoSetBandwidth(uint16_t ulBandwidth, uint16_t dlBandwidth)
1927{
1928 NS_LOG_FUNCTION(this << +ulBandwidth << +dlBandwidth);
1929 NS_ASSERT(ulBandwidth == dlBandwidth);
1930 SetChannelBandwidth(dlBandwidth);
1931}
1932
1933void
1934NrGnbPhy::DoSetEarfcn(uint16_t ulEarfcn, uint16_t dlEarfcn)
1935{
1936 NS_LOG_FUNCTION(this << ulEarfcn << dlEarfcn);
1937}
1938
1939void
1940NrGnbPhy::DoAddUe([[maybe_unused]] uint16_t rnti)
1941{
1942 NS_LOG_FUNCTION(this << rnti);
1943 std::set<uint16_t>::iterator it;
1944 it = m_ueAttachedRnti.find(rnti);
1945 if (it == m_ueAttachedRnti.end())
1946 {
1947 m_ueAttachedRnti.insert(rnti);
1948 }
1949}
1950
1951void
1952NrGnbPhy::DoRemoveUe(uint16_t rnti)
1953{
1954 NS_LOG_FUNCTION(this << rnti);
1955
1956 std::set<uint16_t>::iterator it = m_ueAttachedRnti.find(rnti);
1957 if (it != m_ueAttachedRnti.end())
1958 {
1959 m_ueAttachedRnti.erase(it);
1960 }
1961 else
1962 {
1963 NS_FATAL_ERROR("Impossible to remove UE, not attached!");
1964 }
1965}
1966
1967void
1968NrGnbPhy::DoSetPa(uint16_t rnti, double pa)
1969{
1970 NS_LOG_FUNCTION(this << rnti << pa);
1971}
1972
1973void
1974NrGnbPhy::DoSetTransmissionMode(uint16_t rnti, uint8_t txMode)
1975{
1976 NS_LOG_FUNCTION(this << rnti << +txMode);
1977 // UL supports only SISO MODE
1978}
1979
1980void
1981NrGnbPhy::DoSetSrsConfigurationIndex(uint16_t rnti, uint16_t srcCi)
1982{
1983 NS_LOG_FUNCTION(this << rnti << srcCi);
1984}
1985
1986void
1987NrGnbPhy::DoSetMasterInformationBlock([[maybe_unused]] NrRrcSap::MasterInformationBlock mib)
1988{
1989 NS_LOG_FUNCTION(this);
1990}
1991
1992void
1993NrGnbPhy::DoSetSystemInformationBlockType1(NrRrcSap::SystemInformationBlockType1 sib1)
1994{
1995 NS_LOG_FUNCTION(this);
1996 m_sib1 = sib1;
1997}
1998
1999int8_t
2001{
2002 NS_LOG_FUNCTION(this);
2003 return static_cast<int8_t>(m_txPower);
2004}
2005
2006void
2008{
2009 m_phySapUser = ptr;
2010}
2011
2012void
2014{
2015 NS_LOG_FUNCTION(this);
2016 // forward to scheduler
2017 if (m_ueAttachedRnti.find(mes.m_rnti) != m_ueAttachedRnti.end())
2018 {
2019 NS_LOG_INFO("Received UL HARQ feedback " << mes.IsReceivedOk()
2020 << " and forwarding to the scheduler");
2021 m_phySapUser->UlHarqFeedback(mes);
2022 }
2023}
2024
2025void
2026NrGnbPhy::SetPattern(const std::string& pattern)
2027{
2028 NS_LOG_FUNCTION(this);
2029
2030 static std::unordered_map<std::string, LteNrTddSlotType> lookupTable = {
2031 {"DL", LteNrTddSlotType::DL},
2032 {"UL", LteNrTddSlotType::UL},
2033 {"S", LteNrTddSlotType::S},
2034 {"F", LteNrTddSlotType::F},
2035 };
2036
2037 std::vector<LteNrTddSlotType> vector;
2038 std::stringstream ss(pattern);
2039 std::string token;
2040 std::vector<std::string> extracted;
2041
2042 while (std::getline(ss, token, '|'))
2043 {
2044 extracted.push_back(token);
2045 }
2046
2047 for (const auto& v : extracted)
2048 {
2049 if (lookupTable.find(v) == lookupTable.end())
2050 {
2051 NS_FATAL_ERROR("Pattern type " << v << " not valid. Valid values are: DL UL F S");
2052 }
2053 vector.push_back(lookupTable[v]);
2054 }
2055
2056 SetTddPattern(vector);
2057}
2058
2059std::string
2061{
2063}
2064
2065void
2067{
2068 NS_LOG_FUNCTION(this);
2069 m_isPrimary = true;
2070}
2071
2072void
2074{
2075 m_csiRsModel = csiRsModel;
2076}
2077
2080{
2081 return m_csiRsModel;
2082}
2083
2084void
2085NrGnbPhy::SetCsiRsPeriodicity(uint16_t csiRsPeriodicity)
2086{
2087 m_csiRsPeriodicity = csiRsPeriodicity;
2088}
2089
2090uint16_t
2092{
2093 return m_csiRsPeriodicity;
2094}
2095
2096void
2097NrGnbPhy::ChannelAccessGranted(const Time& time)
2098{
2099 NS_LOG_FUNCTION(this);
2100
2101 if (time < GetSlotPeriod())
2102 {
2103 NS_LOG_INFO("Channel granted for less than the slot time. Ignoring the grant.");
2104 m_channelStatus = NONE;
2105 return;
2106 }
2107
2108 m_channelStatus = GRANTED;
2109
2110 Time toNextSlot = m_lastSlotStart + GetSlotPeriod() - Simulator::Now();
2111 Time grant = time - toNextSlot;
2112 int64_t slotGranted = grant.GetNanoSeconds() / GetSlotPeriod().GetNanoSeconds();
2113
2114 NS_LOG_INFO("Channel access granted for " << time << ", which corresponds to " << slotGranted
2115 << " slot in which each slot is " << GetSlotPeriod()
2116 << ". We lost " << toNextSlot);
2117 NS_ASSERT(!m_channelLostTimer.IsPending());
2118
2119 if (slotGranted < 1)
2120 {
2121 slotGranted = 1;
2122 }
2123 m_channelLostTimer = Simulator::Schedule(GetSlotPeriod() * slotGranted - NanoSeconds(1),
2124 &NrGnbPhy::ChannelAccessLost,
2125 this);
2126}
2127
2128void
2129NrGnbPhy::ChannelAccessLost()
2130{
2131 NS_LOG_FUNCTION(this);
2132 NS_LOG_INFO("Channel access lost");
2133 m_channelStatus = NONE;
2134}
2135
2136} // namespace ns3
Representation of a beam id.
Definition beam-id.h:26
Template for the implementation of the NrFhPhySapUser as a member of an owner class of type C to whic...
@ DL_HARQ
DL HARQ feedback.
@ RACH_PREAMBLE
Random Access Preamble.
@ DL_DCI
The resources allocation map from the BS to the attached UEs (DL)
@ Dropping
Drop DCI + DATA at the PHY Layer.
Service Access Point (SAP) offered by the FhControl instance to the gnb PHY instance.
void GenerateDataCqiReport(const SpectrumValue &sinr)
Generate a DL CQI report.
void SetN1Delay(uint32_t delay)
: Set the minimum processing delay (in slots) to decode DL Data and send Harq feedback.
NrGnbCphySapProvider * GetGnbCphySapProvider()
Get the C PHY SAP provider.
void ChangeToQuasiOmniBeamformingVector()
void ReportUlHarqFeedback(const UlHarqInfo &mes)
Get the HARQ feedback from NrSpectrumPhy and forward it to the scheduler.
int8_t DoGetReferenceSignalPower() const
Get the power of the gnb.
Ptr< NrChAccessManager > GetCam() const
Get the channel access manager for the PHY.
void DoDispose() override
DoDispose method inherited from Object.
Definition nr-gnb-phy.cc:57
bool DoesFhAllocationFit(uint16_t bwpId, uint32_t mcs, uint32_t nRegs, uint8_t dlRank) const
Returns a boolean indicating whether the current allocation can fit in the available FH bandwidth.
void SetPrimary()
Set this PHY as primary.
void SetN0Delay(uint32_t delay)
: Set the minimum processing delay (in slots) to decode DL DCI and decode DL data....
void ScheduleStartEventLoop(uint32_t nodeId, uint16_t frame, uint8_t subframe, uint16_t slot) override
Start the ue Event Loop.
~NrGnbPhy() override
~NrGnbPhy
Definition nr-gnb-phy.cc:52
const SfnSf & GetCurrentSfnSf() const override
Get the current SfnSf.
void AssignCsiRsOffset(const Ptr< NrUeNetDevice > &ueDevice)
void SetCsiRsModel(enum CsiRsModel csiRsModel)
Set the CSI-RS model.
void SetCam(const Ptr< NrChAccessManager > &s)
Set the channel access manager interface for this instance of the PHY.
CsiRsModel
CSI-RS model to be used.
Definition nr-gnb-phy.h:100
@ CSI_RS_PER_UE
CSI-RS per UE periodically.
Definition nr-gnb-phy.h:101
@ CSI_RS_PER_BEAM
CSI-RS per beam periodically.
Definition nr-gnb-phy.h:102
void SetSubChannels(const std::vector< int > &rbIndexVector, size_t nTotalAllocRbs)
Set the Tx power spectral density based on the RB index vector.
BeamId GetBeamId(uint16_t rnti) const override
Get the BeamId for the selected user.
void SetGnbCphySapUser(NrGnbCphySapUser *s)
Set the C PHY SAP user.
bool RegisterUe(uint64_t imsi, const Ptr< NrUeNetDevice > &ueDevice)
Add the UE to the list of this gnb UEs.
NrFhPhySapProvider * m_nrFhPhySapProvider
FH Control SAP provider.
Definition nr-gnb-phy.h:476
void SetCsiRsPeriodicity(uint16_t csiRsPeriodicity)
void SetPattern(const std::string &pattern)
Set the pattern that the gnb will utilize.
void SetN2Delay(uint32_t delay)
: Set the minimum processing delay (in slots) to decode UL DCI and prepare UL data.
NrGnbPhy()
NrGnbPhy constructor. Please use the other one.
Definition nr-gnb-phy.cc:43
uint32_t GetN2Delay() const
: Get the minimum processing delay (in slots) to decode UL DCI and prepare UL data
uint32_t GetN1Delay() const
: Get the minimum processing delay (in slots) to decode DL Data and send Harq feedback
void PhyDataPacketReceived(const Ptr< Packet > &p)
Receive a PHY data packet.
void PhyCtrlMessagesReceived(const Ptr< NrControlMessage > &msg)
Receive a list of CTRL messages.
void SetTxPower(double pow)
Set the transmission power for the UE.
uint16_t GetCsiRsPeriodicity() const
Retrieve CSI-RS periodicity.
enum CsiRsModel GetCsiRsModel() const
Gets the CSI-RS model in use.
double GetTxPower() const override
Retrieve the TX power of the gNB.
void SetPhySapUser(NrGnbPhySapUser *ptr)
Install the PHY SAP user (which is in this case the MAC)
NrFhPhySapUser * m_nrFhPhySapUser
FH Control SAP user.
Definition nr-gnb-phy.h:475
uint32_t GetN0Delay() const
: Get the minimum processing delay (in slots) to decode DL DCI and decode DL Data
uint32_t GetNumRbPerRbg() const override
Retrieve the number of RB per RBG.
std::string GetPattern() const
Retrieve the currently installed pattern.
void ChangeBeamformingVector(Ptr< NrNetDevice > dev)
static TypeId GetTypeId()
Get Type id.
Definition nr-gnb-phy.cc:74
SAP interface between the gNB PHY and the gNB MAC.
Definition nr-phy-sap.h:155
virtual void ReceiveControlMessage(Ptr< NrControlMessage > msg)=0
Receive SendNrControlMessage (PDCCH map, CQI feedbacks) using the ideal control channel.
virtual void SlotUlIndication(const SfnSf &sfn, LteNrTddSlotType slotType)=0
Trigger MAC layer to generate an UL slot for the SfnSf indicated.
virtual void ReceivePhyPdu(Ptr< Packet > p)=0
Notify the MAC of the reception of a new PHY-PDU.
virtual void UlCqiReport(NrMacSchedSapProvider::SchedUlCqiInfoReqParameters ulcqi)=0
Returns to MAC level the UL-CQI evaluated.
virtual void UlHarqFeedback(UlHarqInfo params)=0
Notify the HARQ on the UL transmission status.
virtual void SetCurrentSfn(const SfnSf &sfn)=0
Set the current Sfn. The state machine has advanced by one slot.
virtual uint32_t GetNumRbPerRbg() const =0
PHY requests information from MAC. While MAC normally act as user of PHY services,...
virtual uint8_t GetDlCtrlSymbols() const =0
Retrieve the DL CTRL symbols.
virtual void ReceiveRachPreamble(uint32_t raId)=0
Notify the reception of a RACH preamble on the PRACH.
virtual void SlotDlIndication(const SfnSf &sfn, LteNrTddSlotType slotType)=0
Trigger MAC layer to generate a DL slot for the SfnSf indicated.
virtual std::shared_ptr< DciInfoElementTdma > GetUlCtrlDci() const =0
Retrieve a dci for a UL CTRL allocation.
virtual std::shared_ptr< DciInfoElementTdma > GetDlCtrlDci() const =0
Retrieve a dci for a DL CTRL allocation.
The base class for gNb and UE physical layer.
Definition nr-phy.h:67
Time GetSymbolPeriod() const
Get SymbolPeriod.
Definition nr-phy.cc:858
Ptr< NrSpectrumPhy > m_spectrumPhy
Pointer to the (owned) spectrum phy.
Definition nr-phy.h:558
SlotAllocInfo RetrieveSlotAllocInfo()
Get the head for the slot allocation info, and delete it from the internal list.
Definition nr-phy.cc:788
uint16_t GetCellId() const
Definition nr-phy.cc:657
uint16_t GetNumerology() const
Get the configured numerology.
Definition nr-phy.cc:289
Ptr< NrSpectrumPhy > GetSpectrumPhy() const
Retrieve the SpectrumPhy pointer.
Definition nr-phy.cc:669
void EncodeCtrlMsg(const Ptr< NrControlMessage > &msg)
Take the control messages, and put it in a list that will be sent at the first occasion.
Definition nr-phy.cc:469
void SetNumerology(uint16_t numerology)
Set GNB or UE numerology.
Definition nr-phy.cc:261
virtual void SetTbDecodeLatency(const Time &us)
Configures TB decode latency.
Definition nr-phy.cc:882
std::list< Ptr< NrControlMessage > > m_ctrlMsgs
CTRL messages to be sent.
Definition nr-phy.h:571
void SetRbOverhead(double oh)
Set the bandwidth overhead for calculating the usable RB number.
Definition nr-phy.cc:303
Ptr< PacketBurst > GetPacketBurst(SfnSf sf, uint8_t sym, uint16_t rnti)
Retrieve the PacketBurst at the slot/symbol specified.
Definition nr-phy.cc:364
void SetPowerAllocationType(enum NrSpectrumValueHelper::PowerAllocationType powerAllocationType)
Set power allocation type. There are currently supported two types: one that distributes uniformly en...
Definition nr-phy.cc:432
static std::string GetPattern(const std::vector< LteNrTddSlotType > &pattern)
Get a string representation of a pattern.
Definition nr-phy.cc:413
Time GetSlotPeriod() const
Get the slot period.
Definition nr-phy.cc:321
virtual std::list< Ptr< NrControlMessage > > PopCurrentSlotCtrlMsgs()
Extract and return the message list that is at the beginning of the queue.
Definition nr-phy.cc:610
SlotAllocInfo m_currSlotAllocInfo
Current slot allocation.
Definition nr-phy.h:566
enum NrSpectrumValueHelper::PowerAllocationType GetPowerAllocationType() const
Get the power allocation type.
Definition nr-phy.cc:438
void EnqueueCtrlMsgNow(const Ptr< NrControlMessage > &msg)
Enqueue a CTRL message without considering L1L2CtrlLatency.
Definition nr-phy.cc:452
uint32_t GetChannelBandwidth() const
Retrieve the channel bandwidth, in Hz.
Definition nr-phy.cc:520
virtual Time GetTbDecodeLatency() const
Returns Transport Block decode latency.
Definition nr-phy.cc:888
SlotAllocInfo & PeekSlotAllocInfo(const SfnSf &sfnsf)
Peek the SlotAllocInfo at the SfnSf specified.
Definition nr-phy.cc:817
double GetCentralFrequency() const
Retrieve the frequency (in Hz) of this PHY's channel.
Definition nr-phy.cc:405
double m_noiseFigure
Noise figure (attribute)
Definition nr-phy.h:561
void SetSymbolsPerSlot(uint16_t symbolsPerSlot)
Set the number of symbol per slot.
Definition nr-phy.cc:295
double GetNoiseFigure() const
Get the NoiseFigure value.
Definition nr-phy.cc:876
void SetNoiseFigure(double d)
Set the NoiseFigure value.
Definition nr-phy.cc:865
double m_txPower
Transmission power (attribute)
Definition nr-phy.h:560
void InitializeMessageList()
Initialize the message list.
Definition nr-phy.cc:598
Ptr< SpectrumValue > GetTxPowerSpectralDensity(const std::vector< int > &rbIndexVector)
Definition nr-phy.cc:394
void SetChannelBandwidth(uint16_t bandwidth)
Function to set the channel bandwidth, used also by child classes, i.e., see functions DoSetDlBanwidt...
Definition nr-phy.cc:242
uint16_t GetBwpId() const
Definition nr-phy.cc:651
void PushFrontSlotAllocInfo(const SfnSf &newSfnSf, const SlotAllocInfo &slotAllocInfo)
Store the slot allocation info at the front.
Definition nr-phy.cc:717
Ptr< NrNetDevice > m_netDevice
Pointer to the owner netDevice.
Definition nr-phy.h:557
uint32_t GetL1L2CtrlLatency() const
Definition nr-phy.cc:663
bool HasDlSlot() const
Go through the current pattern and see if at least one slot is DL, F or S.
Definition nr-phy.cc:476
bool SlotAllocInfoExists(const SfnSf &sfnsf) const
Check if the SlotAllocationInfo for that slot exists.
Definition nr-phy.cc:773
uint32_t GetSymbolsPerSlot() const
Get the number of symbols in a slot.
Definition nr-phy.cc:315
std::vector< LteNrTddSlotType > m_tddPattern
Pattern.
Definition nr-phy.h:573
NrPhySapProvider * m_phySapProvider
Pointer to the MAC.
Definition nr-phy.h:568
double GetRbOverhead() const
Get the bandwidth overhead used when calculating the usable RB number.
Definition nr-phy.cc:309
bool HasUlSlot() const
Go through the current pattern and see if at least one slot is UL, F or S.
Definition nr-phy.cc:482
uint32_t GetRbNum() const
Get the number of Resource block configured.
Definition nr-phy.cc:514
std::vector< int > FromRBGBitmaskToRBAssignment(const std::vector< bool > rbgBitmask) const
Transform a MAC-made vector of RBG to a PHY-ready vector of SINR indices.
Definition nr-phy.cc:165
static bool IsTdd(const std::vector< LteNrTddSlotType > &pattern)
Check if a pattern is TDD.
Definition nr-phy.cc:571
void DoDispose() override
DoDispose method inherited from Object.
Definition nr-phy.cc:199
virtual void SetSlotAllocInfo(const SlotAllocInfo &slotAllocInfo)=0
Set a SlotAllocInfo inside the PHY allocations.
The SfnSf class.
Definition sfnsf.h:32
uint8_t GetSubframe() const
GetSubframe.
Definition sfnsf.cc:170
uint64_t Normalize() const
Normalize the SfnSf in slot number.
Definition sfnsf.cc:99
void Add(uint32_t slotN)
Add to this SfnSf a number of slot indicated by the first parameter.
Definition sfnsf.cc:117
uint8_t GetSlot() const
GetSlot.
Definition sfnsf.cc:176
@ F
DL CTRL + DL DATA + UL DATA + UL CTRL.
@ S
DL CTRL + DL DATA + UL CTRL.
@ DL
DL CTRL + DL DATA.
@ UL
UL DATA + UL CTRL.
@ CTRL
Used for DL/UL CTRL.
@ DATA
Used for DL/UL DATA.
@ SRS
Used for SRS (it would be like DCI format 2_3)
DciFormat
Format of the DCI.
The DlCqiInfo struct.
uint16_t m_rnti
The RNTI.
A struct that contains info for the DL HARQ.
uint16_t m_rnti
RNTI.
uint8_t m_symStart
Sym start of the transmission to which this CQI refers to.
MasterInformationBlock structure.
Definition nr-rrc-sap.h:627
uint16_t systemFrameNumber
system frame number
Definition nr-rrc-sap.h:630
uint16_t dlBandwidth
DL bandwidth.
Definition nr-rrc-sap.h:629
SfnSf m_sfnSf
SfnSf of this allocation.
bool ContainsDlCtrlAllocation() const
uint32_t m_numSymAlloc
Number of allocated symbols.
bool ContainsDataAllocation() const
Check if we have data allocations.
@ UL
UL Allocations.
@ DL
DL Allocations.
bool ContainsUlMsg3Allocation() const
Check if we have UL MSG3 allocations.
std::deque< VarTtiAllocInfo > m_varTtiAllocInfo
queue of allocations
A struct that contains info for the UL HARQ.
bool IsReceivedOk() const override