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