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