5G-LENA nr-v3.3-159-ga6832aa7
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-ue-phy.cc
1// Copyright (c) 2011 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2// Copyright (c) 2015 NYU WIRELESS, Tandon School of Engineering, New York University
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-ue-phy.h"
14
15#include "beam-manager.h"
16#include "nr-ch-access-manager.h"
17#include "nr-radio-bearer-tag.h"
18#include "nr-ue-net-device.h"
19#include "nr-ue-power-control.h"
20
21#include "ns3/boolean.h"
22#include "ns3/double.h"
23#include "ns3/enum.h"
24#include "ns3/log.h"
25#include "ns3/node.h"
26#include "ns3/pointer.h"
27#include "ns3/simulator.h"
28
29#include <algorithm>
30#include <cfloat>
31
32namespace ns3
33{
34
35const Time NR_DEFAULT_PMI_INTERVAL_WB{MilliSeconds(10)}; // Wideband PMI update interval
36const Time NR_DEFAULT_PMI_INTERVAL_SB{MilliSeconds(2)}; // Subband PMI update interval
37
38NS_LOG_COMPONENT_DEFINE("NrUePhy");
39NS_OBJECT_ENSURE_REGISTERED(NrUePhy);
40
42{
43 NS_LOG_FUNCTION(this);
44 m_wbCqiLast = Simulator::Now();
45 m_ueCphySapProvider = new MemberNrUeCphySapProvider<NrUePhy>(this);
46 m_powerControl = CreateObject<NrUePowerControl>(this);
47 m_isConnected = false;
48 Simulator::Schedule(m_ueMeasurementsFilterPeriod, &NrUePhy::ReportUeMeasurements, this);
49}
50
52{
53 NS_LOG_FUNCTION(this);
54}
55
56void
58{
59 NS_LOG_FUNCTION(this);
60 delete m_ueCphySapProvider;
61 if (m_powerControl)
62 {
63 m_powerControl->Dispose();
64 m_powerControl = nullptr;
65 }
66 if (m_cam)
67 {
68 m_cam->Dispose();
69 m_cam = nullptr;
70 }
72}
73
74TypeId
76{
77 static TypeId tid =
78 TypeId("ns3::NrUePhy")
79 .SetParent<NrPhy>()
80 .AddConstructor<NrUePhy>()
81 .AddAttribute("TxPower",
82 "Transmission power in dBm",
83 DoubleValue(2.0),
84 MakeDoubleAccessor(&NrUePhy::m_txPower),
85 MakeDoubleChecker<double>())
86 .AddAttribute(
87 "NoiseFigure",
88 "Loss (dB) in the Signal-to-Noise-Ratio due to non-idealities in the receiver."
89 " According to Wikipedia (http://en.wikipedia.org/wiki/Noise_figure), this is "
90 "\"the difference in decibels (dB) between"
91 " the noise output of the actual receiver to the noise output of an "
92 " ideal receiver with the same overall gain and bandwidth when the receivers "
93 " are connected to sources at the standard noise temperature T0.\" "
94 "In this model, we consider T0 = 290K.",
95 DoubleValue(5.0), // nr code from NYU and UniPd assumed in the code the value of
96 // 5dB, that is why we configure the default value to that
97 MakeDoubleAccessor(&NrPhy::SetNoiseFigure, &NrPhy::GetNoiseFigure),
98 MakeDoubleChecker<double>())
99 .AddAttribute(
100 "PowerAllocationType",
101 "Defines the type of the power allocation. Currently are supported "
102 "two types: \"UniformPowerAllocBw\", which is a uniform power allocation over all "
103 "bandwidth (over all RBs), and \"UniformPowerAllocBw\", which is a uniform "
104 "power allocation over used (active) RBs. By default is set a uniform power "
105 "allocation over used RBs .",
106 EnumValue(NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED),
107 MakeEnumAccessor<NrSpectrumValueHelper::PowerAllocationType>(
110 MakeEnumChecker(NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_BW,
111 "UniformPowerAllocBw",
112 NrSpectrumValueHelper::UNIFORM_POWER_ALLOCATION_USED,
113 "UniformPowerAllocUsed"))
114 .AddAttribute("SpectrumPhy",
115 "The SpectrumPhy associated to this NrPhy",
116 TypeId::ATTR_GET,
117 PointerValue(),
118 MakePointerAccessor(&NrPhy::GetSpectrumPhy),
119 MakePointerChecker<NrSpectrumPhy>())
120 .AddAttribute("LBTThresholdForCtrl",
121 "After a DL/UL transmission, if we have less than this value to send the "
122 "UL CTRL, we consider the channel as granted",
123 TimeValue(MicroSeconds(25)),
124 MakeTimeAccessor(&NrUePhy::m_lbtThresholdForCtrl),
125 MakeTimeChecker())
126 .AddAttribute("TbDecodeLatency",
127 "Transport block decode latency",
128 TimeValue(MicroSeconds(100)),
130 MakeTimeChecker())
131 .AddAttribute("EnableUplinkPowerControl",
132 "If true, Uplink Power Control will be enabled.",
133 BooleanValue(false),
134 MakeBooleanAccessor(&NrUePhy::SetEnableUplinkPowerControl),
135 MakeBooleanChecker())
136 .AddAttribute("WbPmiUpdateInterval",
137 "Wideband PMI update interval",
138 TimeValue(NR_DEFAULT_PMI_INTERVAL_WB),
139 MakeTimeAccessor(&NrUePhy::m_wbPmiUpdateInterval),
140 MakeTimeChecker())
141 .AddAttribute("SbPmiUpdateInterval",
142 "Subband PMI update interval",
143 TimeValue(NR_DEFAULT_PMI_INTERVAL_SB),
144 MakeTimeAccessor(&NrUePhy::m_sbPmiUpdateInterval),
145 MakeTimeChecker())
146 .AddAttribute("AlphaCovMat",
147 "The alpha parameter for the calculation of the interference covariance "
148 "matrix moving average",
149 DoubleValue(1),
151 MakeDoubleChecker<double>(0.0, 1))
152 .AddAttribute(
153 "CsiImDuration",
154 "CSI-IM duration in the number of OFDM symbols",
155 UintegerValue(1),
157 MakeUintegerChecker<uint8_t>(1, 12))
158 .AddTraceSource("DlDataSinr",
159 "DL DATA SINR statistics.",
160 MakeTraceSourceAccessor(&NrUePhy::m_dlDataSinrTrace),
161 "ns3::NrUePhy::DlDataSinrTracedCallback")
162 .AddTraceSource("DlCtrlSinr",
163 "Report the SINR computed for DL CTRL",
164 MakeTraceSourceAccessor(&NrUePhy::m_dlCtrlSinrTrace),
165 "ns3::NrUePhy::DlCtrlSinrTracedCallback")
166 .AddAttribute("UeMeasurementsFilterPeriod",
167 "Time period for reporting UE measurements, i.e., the"
168 "length of layer-1 filtering.",
169 TimeValue(MilliSeconds(200)),
170 MakeTimeAccessor(&NrUePhy::m_ueMeasurementsFilterPeriod),
171 MakeTimeChecker())
172 .AddTraceSource("ReportUplinkTbSize",
173 "Report allocated uplink TB size for trace.",
174 MakeTraceSourceAccessor(&NrUePhy::m_reportUlTbSize),
175 "ns3::UlTbSize::TracedCallback")
176 .AddTraceSource("ReportDownlinkTbSize",
177 "Report allocated downlink TB size for trace.",
178 MakeTraceSourceAccessor(&NrUePhy::m_reportDlTbSize),
179 "ns3::DlTbSize::TracedCallback")
180 .AddTraceSource("ReportRsrp",
181 "RSRP statistics.",
182 MakeTraceSourceAccessor(&NrUePhy::m_reportRsrpTrace),
183 "ns3::CurrentRsrp::TracedCallback")
184 .AddTraceSource("UePhyRxedCtrlMsgsTrace",
185 "Ue PHY Control Messages Traces.",
186 MakeTraceSourceAccessor(&NrUePhy::m_phyRxedCtrlMsgsTrace),
187 "ns3::NrPhyRxTrace::RxedUePhyCtrlMsgsTracedCallback")
188 .AddTraceSource("UePhyTxedCtrlMsgsTrace",
189 "Ue PHY Control Messages Traces.",
190 MakeTraceSourceAccessor(&NrUePhy::m_phyTxedCtrlMsgsTrace),
191 "ns3::NrPhyRxTrace::TxedUePhyCtrlMsgsTracedCallback")
192 .AddTraceSource("UePhyRxedDlDciTrace",
193 "Ue PHY DL DCI Traces.",
194 MakeTraceSourceAccessor(&NrUePhy::m_phyUeRxedDlDciTrace),
195 "ns3::NrPhyRxTrace::RxedUePhyDlDciTracedCallback")
196 .AddTraceSource("UePhyTxedHarqFeedbackTrace",
197 "Ue PHY DL HARQ Feedback Traces.",
198 MakeTraceSourceAccessor(&NrUePhy::m_phyUeTxedHarqFeedbackTrace),
199 "ns3::NrPhyRxTrace::TxedUePhyHarqFeedbackTracedCallback")
200 .AddTraceSource("ReportPowerSpectralDensity",
201 "Power Spectral Density data.",
202 MakeTraceSourceAccessor(&NrUePhy::m_reportPowerSpectralDensity),
203 "ns3::NrUePhy::PowerSpectralDensityTracedCallback")
204 .AddTraceSource("CqiFeedbackTrace",
205 "Mimo CQI feedback traces containing RNTI, WB CQI, MCS, and RI ",
206 MakeTraceSourceAccessor(&NrUePhy::m_cqiFeedbackTrace),
207 "ns3::NrUePhy::CqiFeedbackTracedCallback")
208 .AddTraceSource("ReportUeMeasurements",
209 "Report UE measurements RSRP (dBm) and RSRQ (dB).",
210 MakeTraceSourceAccessor(&NrUePhy::m_reportUeMeasurements),
211 "ns3::NrUePhy::RsrpRsrqTracedCallback")
212 .AddAttribute("EnableRlfDetection",
213 "If true, RLF detection will be enabled.",
214 BooleanValue(true),
215 MakeBooleanAccessor(&NrUePhy::m_enableRlfDetection),
216 MakeBooleanChecker());
217 return tid;
218}
219
220void
221NrUePhy::ChannelAccessGranted([[maybe_unused]] const Time& time)
222{
223 NS_LOG_FUNCTION(this);
224 // That will be granted only till the end of the slot
225 m_channelStatus = GRANTED;
226}
227
228void
229NrUePhy::ChannelAccessDenied()
230{
231 NS_LOG_FUNCTION(this);
232 m_channelStatus = NONE;
233}
234
235void
237{
238 NS_LOG_FUNCTION(this);
239 m_ueCphySapUser = s;
240}
241
244{
245 NS_LOG_FUNCTION(this);
246 return (m_ueCphySapProvider);
247}
248
249void
250NrUePhy::SetEnableUplinkPowerControl(bool enable)
251{
252 m_enableUplinkPowerControl = enable;
253}
254
255void
257{
258 m_alphaCovMat = alpha;
259}
260
261double
263{
264 return m_alphaCovMat;
265}
266
267void
268NrUePhy::SetCsiImDuration(uint8_t csiImDuration)
269{
270 m_csiImDuration = csiImDuration;
271}
272
273uint8_t
275{
276 return m_csiImDuration;
277}
278
279void
281{
282 m_txPower = pow;
283 m_powerControl->SetTxPower(pow);
284}
285
286double
288{
289 return m_txPower;
290}
291
292double
294{
295 return m_rsrp;
296}
297
298Ptr<NrUePowerControl>
300{
301 NS_LOG_FUNCTION(this);
302 return m_powerControl;
303}
304
305void
306NrUePhy::SetUplinkPowerControl(Ptr<NrUePowerControl> pc)
307{
308 m_powerControl = pc;
309}
310
311void
312NrUePhy::SetDlAmc(const Ptr<const NrAmc>& amc)
313{
314 m_amc = amc;
315
316 if (m_pmSearch)
317 {
318 m_pmSearch->SetAmc(amc);
319 }
320}
321
322void
323NrUePhy::SetSubChannelsForTransmission(const std::vector<int>& mask, uint32_t numSym)
324{
325 Ptr<SpectrumValue> txPsd = GetTxPowerSpectralDensity(mask);
326 NS_ASSERT(txPsd);
327
328 m_reportPowerSpectralDensity(m_currentSlot,
329 txPsd,
330 numSym * GetSymbolPeriod(),
331 m_rnti,
332 m_imsi,
333 GetBwpId(),
334 GetCellId());
335 m_spectrumPhy->SetTxPowerSpectralDensity(txPsd);
336}
337
338void
339NrUePhy::DoSendControlMessage(Ptr<NrControlMessage> msg)
340{
341 NS_LOG_FUNCTION(this << msg);
343}
344
345void
346NrUePhy::DoSendControlMessageNow(Ptr<NrControlMessage> msg)
347{
348 NS_LOG_FUNCTION(this << msg);
350}
351
352void
353NrUePhy::ProcessDataDci(const SfnSf& ulSfnSf,
354 const std::shared_ptr<DciInfoElementTdma>& dciInfoElem)
355{
356 NS_LOG_FUNCTION(this);
357
358 NS_LOG_DEBUG("UE" << m_rnti << " UL-DCI received for slot " << ulSfnSf << " symStart "
359 << static_cast<uint32_t>(dciInfoElem->m_symStart) << " numSym "
360 << static_cast<uint32_t>(dciInfoElem->m_numSym) << " tbs "
361 << dciInfoElem->m_tbSize << " harqId "
362 << static_cast<uint32_t>(dciInfoElem->m_harqProcess));
363
364 if (ulSfnSf == m_currentSlot)
365 {
366 InsertAllocation(dciInfoElem);
367 }
368 else
369 {
370 InsertFutureAllocation(ulSfnSf, dciInfoElem);
371 }
372}
373
374void
375NrUePhy::SendRachPreamble(uint32_t PreambleId, uint32_t Rnti)
376{
377 NS_LOG_FUNCTION(this << PreambleId);
378 m_raPreambleId = PreambleId;
379 Ptr<NrRachPreambleMessage> msg = Create<NrRachPreambleMessage>();
380 msg->SetSourceBwp(GetBwpId());
381 msg->SetRapId(PreambleId);
383}
384
385void
386NrUePhy::ProcessSrsDci(const SfnSf& ulSfnSf, const std::shared_ptr<DciInfoElementTdma>& dciInfoElem)
387{
388 NS_LOG_FUNCTION(this);
389 // Instruct PHY for transmitting the SRS
390 if (ulSfnSf == m_currentSlot)
391 {
392 InsertAllocation(dciInfoElem);
393 }
394 else
395 {
396 InsertFutureAllocation(ulSfnSf, dciInfoElem);
397 }
398}
399
400void
402{
403 NS_LOG_FUNCTION(this);
404
406 DoSetCellId(bwpId);
407}
408
409void
410NrUePhy::SetUlCtrlSyms(uint8_t ulCtrlSyms)
411{
412 m_ulCtrlSyms = ulCtrlSyms;
413}
414
415void
416NrUePhy::SetDlCtrlSyms(uint8_t dlCtrlSyms)
417{
418 m_dlCtrlSyms = dlCtrlSyms;
419}
420
421void
422NrUePhy::SetNumRbPerRbg(uint32_t numRbPerRbg)
423{
424 m_numRbPerRbg = numRbPerRbg;
425}
426
427void
428NrUePhy::SetPattern(const std::string& pattern)
429{
430 NS_LOG_FUNCTION(this);
431
432 static std::unordered_map<std::string, LteNrTddSlotType> lookupTable = {
433 {"DL", LteNrTddSlotType::DL},
434 {"UL", LteNrTddSlotType::UL},
435 {"S", LteNrTddSlotType::S},
436 {"F", LteNrTddSlotType::F},
437 };
438
439 std::vector<LteNrTddSlotType> vector;
440 std::stringstream ss(pattern);
441 std::string token;
442 std::vector<std::string> extracted;
443
444 while (std::getline(ss, token, '|'))
445 {
446 extracted.push_back(token);
447 }
448
449 vector.reserve(extracted.size());
450 for (const auto& v : extracted)
451 {
452 vector.push_back(lookupTable[v]);
453 }
454
455 m_tddPattern = vector;
456 // Check if pure UL BWP
457 const auto ulSlots = std::count(m_tddPattern.begin(), m_tddPattern.end(), LteNrTddSlotType::UL);
458 if (static_cast<size_t>(ulSlots) == m_tddPattern.size())
459 {
460 // In case Downlink CSI feedback is enabled, disable it
461 m_csiFeedbackType = 0;
462 }
463}
464
465uint32_t
467{
468 return m_numRbPerRbg;
469}
470
471void
472NrUePhy::SetCurrentSfnSf(const SfnSf& currentSfnSf)
473{
474 m_currentSlot = currentSfnSf;
475}
476
477void
479{
480 m_lastSlotStart = startTime;
481}
482
483Time
485{
486 return m_lastSlotStart;
487}
488
491{
492 return m_phySapUser;
493}
494
495double
496NrUePhy::ComputeAvgSinr(const SpectrumValue& sinr)
497{
498 // averaged SINR among RBs
499 double sum = 0.0;
500 uint16_t rbNum = 0;
501 Values::const_iterator it;
502
503 for (it = sinr.ConstValuesBegin(); it != sinr.ConstValuesEnd(); it++)
504 {
505 sum += (*it);
506 rbNum++;
507 }
508
509 double avrgSinr = (rbNum > 0) ? (sum / rbNum) : DBL_MAX;
510
511 return avrgSinr;
512}
513
514void
515NrUePhy::InsertAllocation(const std::shared_ptr<DciInfoElementTdma>& dci)
516{
517 NS_LOG_FUNCTION(this);
518
519 VarTtiAllocInfo varTtiInfo(dci);
520 m_currSlotAllocInfo.m_varTtiAllocInfo.push_back(varTtiInfo);
521 std::stable_sort(m_currSlotAllocInfo.m_varTtiAllocInfo.begin(),
523}
524
525void
526NrUePhy::InsertFutureAllocation(const SfnSf& sfnSf, const std::shared_ptr<DciInfoElementTdma>& dci)
527{
528 NS_LOG_FUNCTION(this);
529
530 VarTtiAllocInfo varTtiInfo(dci);
531 if (SlotAllocInfoExists(sfnSf))
532 {
533 auto& ulSlot = PeekSlotAllocInfo(sfnSf);
534 ulSlot.m_varTtiAllocInfo.push_back(varTtiInfo);
535 std::stable_sort(ulSlot.m_varTtiAllocInfo.begin(), ulSlot.m_varTtiAllocInfo.end());
536 }
537 else
538 {
539 SlotAllocInfo slotAllocInfo = SlotAllocInfo(sfnSf);
540 slotAllocInfo.m_varTtiAllocInfo.push_back(varTtiInfo);
541 PushBackSlotAllocInfo(slotAllocInfo);
542 }
543}
544
545void
546NrUePhy::PhyCtrlMessagesReceived(const Ptr<NrControlMessage>& msg)
547{
548 NS_LOG_FUNCTION(this);
549
550 if (msg->GetMessageType() == NrControlMessage::DL_DCI)
551 {
552 auto dciMsg = DynamicCast<NrDlDciMessage>(msg);
553 auto dciInfoElem = dciMsg->GetDciInfoElement();
554
555 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), msg);
556
557 if (dciInfoElem->m_rnti != 0 && dciInfoElem->m_rnti != m_rnti)
558 {
559 return; // DCI not for me
560 }
561
562 SfnSf dciSfn = m_currentSlot;
563 uint32_t k0Delay = dciMsg->GetKDelay();
564 dciSfn.Add(k0Delay);
565
566 NS_LOG_DEBUG("UE" << m_rnti << " DL-DCI received for slot " << dciSfn << " symStart "
567 << static_cast<uint32_t>(dciInfoElem->m_symStart) << " numSym "
568 << static_cast<uint32_t>(dciInfoElem->m_numSym) << " tbs "
569 << dciInfoElem->m_tbSize << " harqId "
570 << static_cast<uint32_t>(dciInfoElem->m_harqProcess));
571
572 /* BIG ASSUMPTION: We assume that K0 is always 0 */
573
574 auto it = m_harqIdToK1Map.find(dciInfoElem->m_harqProcess);
575 if (it != m_harqIdToK1Map.end())
576 {
577 m_harqIdToK1Map.erase(m_harqIdToK1Map.find(dciInfoElem->m_harqProcess));
578 }
579
580 m_harqIdToK1Map.insert(std::make_pair(dciInfoElem->m_harqProcess, dciMsg->GetK1Delay()));
581
582 m_phyUeRxedDlDciTrace(m_currentSlot,
583 GetCellId(),
584 m_rnti,
585 GetBwpId(),
586 dciInfoElem->m_harqProcess,
587 dciMsg->GetK1Delay());
588
589 InsertAllocation(dciInfoElem);
590
591 m_phySapUser->ReceiveControlMessage(msg);
592
593 if (m_enableUplinkPowerControl)
594 {
595 m_powerControl->ReportTpcPusch(dciInfoElem->m_tpc);
596 m_powerControl->ReportTpcPucch(dciInfoElem->m_tpc);
597 }
598 }
599 else if (msg->GetMessageType() == NrControlMessage::UL_DCI)
600 {
601 auto dciMsg = DynamicCast<NrUlDciMessage>(msg);
602 auto dciInfoElem = dciMsg->GetDciInfoElement();
603
604 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), msg);
605
606 if (dciInfoElem->m_rnti != 0 && dciInfoElem->m_rnti != m_rnti)
607 {
608 return; // DCI not for me
609 }
610
611 SfnSf ulSfnSf = m_currentSlot;
612 uint32_t k2Delay = dciMsg->GetKDelay();
613 ulSfnSf.Add(k2Delay);
614
615 if (dciInfoElem->m_type == DciInfoElementTdma::DATA)
616 {
617 ProcessDataDci(ulSfnSf, dciInfoElem);
618 m_phySapUser->ReceiveControlMessage(msg);
619 }
620 else if (dciInfoElem->m_type == DciInfoElementTdma::SRS)
621 {
622 ProcessSrsDci(ulSfnSf, dciInfoElem);
623 // Do not pass the DCI to MAC
624 }
625 }
626 else if (msg->GetMessageType() == NrControlMessage::MIB)
627 {
628 NS_LOG_DEBUG("received MIB");
629 Ptr<NrMibMessage> msg2 = DynamicCast<NrMibMessage>(msg);
630 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), msg);
631 m_ueCphySapUser->RecvMasterInformationBlock(GetCellId(), msg2->GetMib());
632 }
633 else if (msg->GetMessageType() == NrControlMessage::SIB1)
634 {
635 Ptr<NrSib1Message> msg2 = DynamicCast<NrSib1Message>(msg);
636 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), msg);
637 m_ueCphySapUser->RecvSystemInformationBlockType1(GetCellId(), msg2->GetSib1());
638 }
639 else if (msg->GetMessageType() == NrControlMessage::RAR)
640 {
641 Ptr<NrRarMessage> rarMsg = DynamicCast<NrRarMessage>(msg);
642
643 ProcessRar(rarMsg);
644 }
645 else
646 {
647 NS_LOG_INFO("Message type not recognized " << msg->GetMessageType());
648 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), msg);
649 m_phySapUser->ReceiveControlMessage(msg);
650 }
651}
652
653void
654NrUePhy::ProcessRar(const Ptr<NrRarMessage>& rarMsg)
655{
656 NS_LOG_FUNCTION(this);
657 bool myRar = false;
658 {
659 for (auto it = rarMsg->RarListBegin(); it != rarMsg->RarListEnd(); ++it)
660 {
661 NS_LOG_INFO("Received RAR in slot" << m_currentSlot << " with RA preamble ID: "
662 << std::to_string(it->rarPayload.raPreambleId));
663 if (it->rarPayload.raPreambleId == m_raPreambleId)
664 {
665 NS_LOG_INFO("Received RAR with RA preamble ID:" << +it->rarPayload.raPreambleId
666 << " current RA preamble ID is :"
667 << m_raPreambleId);
668 // insert allocation
669 SfnSf ulSfnSf = m_currentSlot;
670 uint32_t k2Delay = it->rarPayload.k2Delay;
671 ulSfnSf.Add(k2Delay);
672 NS_LOG_DEBUG("Insert RAR UL DCI allocation for " << ulSfnSf);
673 ProcessDataDci(ulSfnSf, (*it).rarPayload.ulMsg3Dci);
674 myRar = true;
675 // notify MAC and above about transmission opportunity
676 m_phySapUser->ReceiveControlMessage(rarMsg);
677 // fire CTRL msg trace
678 m_phyRxedCtrlMsgsTrace(m_currentSlot, GetCellId(), m_rnti, GetBwpId(), rarMsg);
679 // reset RACH variables with out of range values
680 m_raPreambleId = 255;
681 }
682 }
683 if (!myRar)
684 {
685 NS_LOG_DEBUG("Skipping RAR, does not contain preamble ID."
686 << "\n My preamble id: " << std::to_string(m_raPreambleId) << " found:");
687 for (auto it = rarMsg->RarListBegin(); it != rarMsg->RarListEnd(); ++it)
688 {
689 NS_LOG_DEBUG("rapId: " << std::to_string(it->rapId));
690 }
691 }
692 }
693}
694
695void
696NrUePhy::TryToPerformLbt()
697{
698 NS_LOG_FUNCTION(this);
699 uint8_t ulCtrlSymStart = 0;
700 uint8_t ulCtrlNumSym = 0;
701
702 for (const auto& alloc : m_currSlotAllocInfo.m_varTtiAllocInfo)
703 {
704 if (alloc.m_dci->m_type == DciInfoElementTdma::CTRL &&
705 alloc.m_dci->m_format == DciInfoElementTdma::UL)
706 {
707 ulCtrlSymStart = alloc.m_dci->m_symStart;
708 ulCtrlNumSym = alloc.m_dci->m_numSym;
709 break;
710 }
711 }
712
713 if (ulCtrlNumSym != 0)
714 {
715 // We have an UL CTRL symbol scheduled and we have to transmit CTRLs..
716 // .. so we check that we have at least 25 us between the latest DCI,
717 // or we have to schedule an LBT event.
718
719 Time limit = m_lastSlotStart + GetSlotPeriod() -
720 ((GetSymbolsPerSlot() - ulCtrlSymStart) * GetSymbolPeriod()) -
721 m_lbtThresholdForCtrl;
722
723 for (const auto& alloc : m_currSlotAllocInfo.m_varTtiAllocInfo)
724 {
725 int64_t symbolPeriod = GetSymbolPeriod().GetMicroSeconds();
726 int64_t dciEndsAt = m_lastSlotStart.GetMicroSeconds() +
727 ((alloc.m_dci->m_numSym + alloc.m_dci->m_symStart) * symbolPeriod);
728
729 if (alloc.m_dci->m_type != DciInfoElementTdma::DATA &&
730 alloc.m_dci->m_type != DciInfoElementTdma::MSG3)
731 {
732 continue;
733 }
734
735 if (limit.GetMicroSeconds() < dciEndsAt)
736 {
737 NS_LOG_INFO("This data DCI ends at "
738 << MicroSeconds(dciEndsAt)
739 << " which is inside the LBT shared COT (the limit is " << limit
740 << "). No need for LBT");
741 m_lbtEvent.Cancel(); // Forget any LBT we previously set, because of the new
742 // DCI information
743 m_channelStatus = GRANTED;
744 }
745 else
746 {
747 NS_LOG_INFO("This data DCI starts at "
748 << +alloc.m_dci->m_symStart << " for " << +alloc.m_dci->m_numSym
749 << " ends at " << MicroSeconds(dciEndsAt)
750 << " which is outside the LBT shared COT (the limit is " << limit
751 << ").");
752 }
753 }
754 if (m_channelStatus != GRANTED)
755 {
756 Time sched = m_lastSlotStart - Simulator::Now() + (GetSymbolPeriod() * ulCtrlSymStart) -
757 MicroSeconds(25);
758 NS_LOG_DEBUG("Scheduling an LBT for sending the UL CTRL at "
759 << Simulator::Now() + sched);
760 m_lbtEvent.Cancel();
761 m_lbtEvent = Simulator::Schedule(sched, &NrUePhy::RequestAccess, this);
762 }
763 else
764 {
765 NS_LOG_DEBUG("Not scheduling LBT: the UE has a channel status that is GRANTED");
766 }
767 }
768 else
769 {
770 NS_LOG_DEBUG("Not scheduling LBT; the UE has no UL CTRL symbols available");
771 }
772}
773
774void
775NrUePhy::RequestAccess()
776{
777 NS_LOG_FUNCTION(this);
778 NS_LOG_DEBUG("Request access because we have to transmit UL CTRL");
779 m_cam->RequestAccess(); // This will put the m_channelStatus to granted when
780 // the channel will be granted.
781}
782
783void
784NrUePhy::PushCtrlAllocations(const SfnSf currentSfnSf)
785{
786 NS_LOG_FUNCTION(this);
787
788 // The UE does not know anything from the GNB yet, so listen on the default
789 // bandwidth.
790 std::vector<bool> rbgBitmask(GetRbNum(), true);
791
792 // The UE still doesn't know the TDD pattern, so just add a DL CTRL
793 if (m_tddPattern.empty())
794 {
795 NS_LOG_INFO("TDD Pattern unknown, insert DL CTRL at the beginning of the slot");
796 VarTtiAllocInfo dlCtrlSlot(std::make_shared<DciInfoElementTdma>(0,
797 m_dlCtrlSyms,
800 rbgBitmask));
801 m_currSlotAllocInfo.m_varTtiAllocInfo.push_front(dlCtrlSlot);
802 return;
803 }
804
805 uint64_t currentSlotN = currentSfnSf.Normalize() % m_tddPattern.size();
806
807 if (m_tddPattern[currentSlotN] < LteNrTddSlotType::UL)
808 {
809 NS_LOG_DEBUG("The current TDD pattern indicates that we are in a "
810 << m_tddPattern[currentSlotN]
811 << " slot, so insert DL CTRL at the beginning of the slot");
812 VarTtiAllocInfo dlCtrlSlot(std::make_shared<DciInfoElementTdma>(0,
813 m_dlCtrlSyms,
816 rbgBitmask));
817 m_currSlotAllocInfo.m_varTtiAllocInfo.push_front(dlCtrlSlot);
818 }
819 if (m_tddPattern[currentSlotN] > LteNrTddSlotType::DL)
820 {
821 NS_LOG_DEBUG("The current TDD pattern indicates that we are in a "
822 << m_tddPattern[currentSlotN]
823 << " slot, so insert UL CTRL at the end of the slot");
824 VarTtiAllocInfo ulCtrlSlot(
825 std::make_shared<DciInfoElementTdma>(GetSymbolsPerSlot() - m_ulCtrlSyms,
826 m_ulCtrlSyms,
829 rbgBitmask));
830 m_currSlotAllocInfo.m_varTtiAllocInfo.push_back(ulCtrlSlot);
831 }
832}
833
834void
835NrUePhy::StartSlot(const SfnSf& s)
836{
837 NS_LOG_FUNCTION(this);
838 m_currentSlot = s;
839 m_lastSlotStart = Simulator::Now();
840
841 // Call MAC before doing anything in PHY
842 m_phySapUser->SlotIndication(m_currentSlot); // trigger mac
843
844 // update the current slot object, and insert DL/UL CTRL allocations depending on the TDD
845 // pattern
846 bool nrAllocationExists = SlotAllocInfoExists(m_currentSlot);
847 FinishSlotProcessing(s, nrAllocationExists);
848}
849
850void
851NrUePhy::FinishSlotProcessing(const SfnSf& s, bool nrAllocationExists)
852{
853 NS_LOG_FUNCTION(this << s);
854 if (nrAllocationExists)
855 {
857 }
858 else
859 {
860 m_currSlotAllocInfo = SlotAllocInfo(m_currentSlot);
861 }
862
863 PushCtrlAllocations(m_currentSlot);
864 NS_ASSERT(m_currSlotAllocInfo.m_sfnSf == m_currentSlot);
865
866 NS_LOG_DEBUG("UE " << m_rnti << " start slot " << m_currSlotAllocInfo.m_sfnSf
867 << " composed by the following allocations, total "
869 for (const auto& alloc : m_currSlotAllocInfo.m_varTtiAllocInfo)
870 {
871 std::string direction;
872 std::string type;
873
874 if (alloc.m_dci->m_format == DciInfoElementTdma::UL)
875 {
876 direction = "UL";
877 }
878 else
879 {
880 direction = "DL";
881 }
882
883 switch (alloc.m_dci->m_type)
884 {
886 type = "SRS";
887 NS_LOG_DEBUG("Allocation from sym "
888 << static_cast<uint32_t>(alloc.m_dci->m_symStart) << " to sym "
889 << static_cast<uint32_t>(alloc.m_dci->m_numSym + alloc.m_dci->m_symStart)
890 << " direction " << direction << " type " << type);
891 break;
893 type = "DATA";
894 NS_LOG_INFO("Allocation from sym "
895 << static_cast<uint32_t>(alloc.m_dci->m_symStart) << " to sym "
896 << static_cast<uint32_t>(alloc.m_dci->m_numSym + alloc.m_dci->m_symStart)
897 << " direction " << direction << " type " << type);
898 break;
900 type = "CTRL";
901 NS_LOG_DEBUG("Allocation from sym "
902 << static_cast<uint32_t>(alloc.m_dci->m_symStart) << " to sym "
903 << static_cast<uint32_t>(alloc.m_dci->m_numSym + alloc.m_dci->m_symStart)
904 << " direction " << direction << " type " << type);
905 break;
907 type = "MSG3";
908 NS_LOG_DEBUG("Allocation from sym "
909 << static_cast<uint32_t>(alloc.m_dci->m_symStart) << " to sym "
910 << static_cast<uint32_t>(alloc.m_dci->m_numSym + alloc.m_dci->m_symStart)
911 << " direction " << direction << " type " << type);
912 break;
913 default:
914 NS_LOG_ERROR("Unknown type DciInfoElementTdma::VarTtiType " << alloc.m_dci->m_type);
915 }
916 }
917
918 TryToPerformLbt();
919
922
923 auto nextVarTtiStart = GetSymbolPeriod() * allocation.m_dci->m_symStart;
924
925 auto ctrlMsgs = PopCurrentSlotCtrlMsgs();
926 if (m_netDevice)
927 {
928 DynamicCast<NrUeNetDevice>(m_netDevice)->RouteOutgoingCtrlMsgs(ctrlMsgs, GetBwpId());
929 }
930 else
931 {
932 // No netDevice (that could happen in tests) so just redirect them to us
933 for (const auto& msg : ctrlMsgs)
934 {
935 EncodeCtrlMsg(msg);
936 }
937 }
938
939 Simulator::Schedule(nextVarTtiStart, &NrUePhy::StartVarTti, this, allocation.m_dci);
940}
941
942Time
943NrUePhy::DlCtrl(const std::shared_ptr<DciInfoElementTdma>& dci)
944{
945 NS_LOG_FUNCTION(this);
946
947 Time varTtiDuration = GetSymbolPeriod() * dci->m_numSym;
948
949 NS_LOG_DEBUG("UE" << m_rnti
950 << " RXing DL CTRL frame for"
951 " symbols "
952 << +dci->m_symStart << "-" << +(dci->m_symStart + dci->m_numSym - 1)
953 << "\t start " << Simulator::Now() << " end "
954 << (Simulator::Now() + varTtiDuration));
955
956 m_tryToPerformLbt = true;
957
958 m_spectrumPhy->AddExpectedDlCtrlEnd(Simulator::Now() + varTtiDuration);
959
960 return varTtiDuration;
961}
962
963Time
964NrUePhy::UlSrs(const std::shared_ptr<DciInfoElementTdma>& dci)
965{
966 NS_LOG_FUNCTION(this);
967
968 std::vector<int> channelRbs;
969 for (uint32_t i = 0; i < GetRbNum(); i++)
970 {
971 channelRbs.push_back(static_cast<int>(i));
972 }
973 SetSubChannelsForTransmission(channelRbs, dci->m_numSym);
974
975 std::list<Ptr<NrControlMessage>> srsMsg;
976 Ptr<NrSrsMessage> srs = Create<NrSrsMessage>();
977 srs->SetSourceBwp(GetBwpId());
978 srsMsg.emplace_back(srs);
979 Time varTtiDuration = GetSymbolPeriod() * dci->m_numSym;
980
981 m_phyTxedCtrlMsgsTrace(m_currentSlot, GetCellId(), dci->m_rnti, GetBwpId(), *srsMsg.begin());
982 m_spectrumPhy->StartTxUlControlFrames(srsMsg, varTtiDuration - NanoSeconds(1.0));
983
984 NS_LOG_DEBUG("UE" << m_rnti << " TXing UL SRS frame for symbols " << +dci->m_symStart << "-"
985 << +(dci->m_symStart + dci->m_numSym - 1) << "\t start " << Simulator::Now()
986 << " end " << (Simulator::Now() + varTtiDuration - NanoSeconds(1.0)));
987
988 ChannelAccessDenied(); // Reset the channel status
989 return varTtiDuration;
990}
991
992Time
993NrUePhy::UlCtrl(const std::shared_ptr<DciInfoElementTdma>& dci)
994{
995 NS_LOG_FUNCTION(this);
996
997 Time varTtiDuration = GetSymbolPeriod() * dci->m_numSym;
998
999 if (m_ctrlMsgs.empty())
1000 {
1001 NS_LOG_DEBUG("UE" << m_rnti << " reserved space for UL CTRL frame for symbols "
1002 << +dci->m_symStart << "-" << +(dci->m_symStart + dci->m_numSym - 1)
1003 << "\t start " << Simulator::Now() << " end "
1004 << (Simulator::Now() + varTtiDuration - NanoSeconds(1.0))
1005 << " but no data to transmit");
1006 m_cam->Cancel();
1007 return varTtiDuration;
1008 }
1009 else if (m_channelStatus != GRANTED)
1010 {
1011 NS_LOG_INFO("UE" << m_rnti << " has to transmit CTRL but channel not granted");
1012 m_cam->Cancel();
1013 return varTtiDuration;
1014 }
1015
1016 for (const auto& msg : m_ctrlMsgs)
1017 {
1018 m_phyTxedCtrlMsgsTrace(m_currentSlot, GetCellId(), dci->m_rnti, GetBwpId(), msg);
1019
1020 if (msg->GetMessageType() == NrControlMessage::DL_HARQ)
1021 {
1022 Ptr<NrDlHarqFeedbackMessage> harqMsg = DynamicCast<NrDlHarqFeedbackMessage>(msg);
1023 uint8_t harqId = harqMsg->GetDlHarqFeedback().m_harqProcessId;
1024
1025 auto it = m_harqIdToK1Map.find(harqId);
1026 if (it != m_harqIdToK1Map.end())
1027 {
1028 m_phyUeTxedHarqFeedbackTrace(m_currentSlot,
1029 GetCellId(),
1030 m_rnti,
1031 GetBwpId(),
1032 static_cast<uint32_t>(harqId),
1033 it->second);
1034 }
1035 }
1036 }
1037
1038 std::vector<int> channelRbs;
1039 for (uint32_t i = 0; i < GetRbNum(); i++)
1040 {
1041 channelRbs.push_back(static_cast<int>(i));
1042 }
1043
1044 if (m_enableUplinkPowerControl)
1045 {
1046 m_txPower = m_powerControl->GetPucchTxPower(channelRbs.size());
1047 }
1048 SetSubChannelsForTransmission(channelRbs, dci->m_numSym);
1049
1050 NS_LOG_DEBUG("UE" << m_rnti << " TXing UL CTRL frame for symbols " << +dci->m_symStart << "-"
1051 << +(dci->m_symStart + dci->m_numSym - 1) << "\t start " << Simulator::Now()
1052 << " end " << (Simulator::Now() + varTtiDuration - NanoSeconds(1.0)));
1053
1054 SendCtrlChannels(varTtiDuration - NanoSeconds(1.0));
1055
1056 ChannelAccessDenied(); // Reset the channel status
1057 return varTtiDuration;
1058}
1059
1060Time
1061NrUePhy::DlData(const std::shared_ptr<DciInfoElementTdma>& dci)
1062{
1063 NS_LOG_FUNCTION(this);
1064
1065 m_receptionEnabled = true;
1066 Time varTtiDuration = GetSymbolPeriod() * dci->m_numSym;
1067 NS_ASSERT(dci->m_rnti == m_rnti);
1068 m_spectrumPhy->AddExpectedTb({dci->m_ndi,
1069 dci->m_tbSize,
1070 dci->m_mcs,
1071 dci->m_rank,
1072 dci->m_rnti,
1073 FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask),
1074 dci->m_harqProcess,
1075 dci->m_rv,
1076 true,
1077 dci->m_symStart,
1078 dci->m_numSym,
1079 m_currentSlot});
1080 m_reportDlTbSize(m_netDevice->GetObject<NrUeNetDevice>()->GetImsi(), dci->m_tbSize);
1081 NS_LOG_INFO("UE" << m_rnti << " RXing DL DATA frame for symbols " << +dci->m_symStart << "-"
1082 << +(dci->m_symStart + dci->m_numSym - 1) << " num of rbg assigned: "
1083 << FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask).size()
1084 << ". RX will take place for " << varTtiDuration);
1085
1086 return varTtiDuration;
1087}
1088
1089Time
1090NrUePhy::UlData(const std::shared_ptr<DciInfoElementTdma>& dci)
1091{
1092 NS_LOG_FUNCTION(this);
1093 if (m_enableUplinkPowerControl)
1094 {
1095 m_txPower = m_powerControl->GetPuschTxPower(
1096 (FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask)).size());
1097 }
1098 SetSubChannelsForTransmission(FromRBGBitmaskToRBAssignment(dci->m_rbgBitmask), dci->m_numSym);
1099 Time varTtiDuration = GetSymbolPeriod() * dci->m_numSym;
1100 std::list<Ptr<NrControlMessage>> ctrlMsg;
1101 Ptr<PacketBurst> pktBurst = GetPacketBurst(m_currentSlot, dci->m_symStart, dci->m_rnti);
1102 if (pktBurst && pktBurst->GetNPackets() > 0)
1103 {
1104 std::list<Ptr<Packet>> pkts = pktBurst->GetPackets();
1105 NrRadioBearerTag bearerTag;
1106 if (!pkts.front()->PeekPacketTag(bearerTag))
1107 {
1108 NS_FATAL_ERROR("No radio bearer tag");
1109 }
1110 }
1111 else
1112 {
1113 // put an error, as something is wrong. The UE should not be scheduled
1114 // if there is no data for him...
1115 if (dci->m_type != DciInfoElementTdma::MSG3)
1116 {
1117 NS_FATAL_ERROR("The UE " << dci->m_rnti << " has been scheduled without data");
1118 }
1119 else
1120 {
1121 NS_LOG_WARN("Not sending MSG3. Probably in RRC IDEAL mode.");
1122 return varTtiDuration;
1123 }
1124 }
1125 m_reportUlTbSize(m_netDevice->GetObject<NrUeNetDevice>()->GetImsi(), dci->m_tbSize);
1126
1127 NS_LOG_DEBUG("UE" << m_rnti << " TXing UL DATA frame for"
1128 << " symbols " << +dci->m_symStart << "-"
1129 << +(dci->m_symStart + dci->m_numSym - 1) << "\t start " << Simulator::Now()
1130 << " end " << (Simulator::Now() + varTtiDuration));
1131
1132 Simulator::Schedule(NanoSeconds(1.0),
1133 &NrUePhy::SendDataChannels,
1134 this,
1135 pktBurst,
1136 ctrlMsg,
1137 dci,
1138 varTtiDuration - NanoSeconds(2.0));
1139 return varTtiDuration;
1140}
1141
1142void
1143NrUePhy::StartVarTti(const std::shared_ptr<DciInfoElementTdma>& dci)
1144{
1145 NS_LOG_FUNCTION(this);
1146 Time varTtiDuration;
1147
1148 m_currTbs = dci->m_tbSize;
1149 m_receptionEnabled = false;
1150
1151 if (dci->m_type == DciInfoElementTdma::CTRL && dci->m_format == DciInfoElementTdma::DL)
1152 {
1153 varTtiDuration = DlCtrl(dci);
1154 }
1155 else if (dci->m_type == DciInfoElementTdma::CTRL && dci->m_format == DciInfoElementTdma::UL)
1156 {
1157 varTtiDuration = UlCtrl(dci);
1158 }
1159 else if (dci->m_type == DciInfoElementTdma::SRS && dci->m_format == DciInfoElementTdma::UL)
1160 {
1161 varTtiDuration = UlSrs(dci);
1162 }
1163 else if (dci->m_type == DciInfoElementTdma::DATA && dci->m_format == DciInfoElementTdma::DL)
1164 {
1165 varTtiDuration = DlData(dci);
1166 }
1167 else if ((dci->m_type == DciInfoElementTdma::DATA || dci->m_type == DciInfoElementTdma::MSG3) &&
1168 dci->m_format == DciInfoElementTdma::UL)
1169 {
1170 varTtiDuration = UlData(dci);
1171 }
1172
1173 Simulator::Schedule(varTtiDuration, &NrUePhy::EndVarTti, this, dci);
1174}
1175
1176void
1177NrUePhy::EndVarTti(const std::shared_ptr<DciInfoElementTdma>& dci)
1178{
1179 NS_LOG_FUNCTION(this);
1180 NS_LOG_DEBUG("DCI started at symbol "
1181 << static_cast<uint32_t>(dci->m_symStart) << " which lasted for "
1182 << static_cast<uint32_t>(dci->m_numSym) << " symbols finished");
1183
1184 if (m_tryToPerformLbt)
1185 {
1186 TryToPerformLbt();
1187 m_tryToPerformLbt = false;
1188 }
1189
1191 {
1192 // end of slot
1193 m_currentSlot.Add(1);
1194
1195 Simulator::Schedule(m_lastSlotStart + GetSlotPeriod() - Simulator::Now(),
1196 &NrUePhy::StartSlot,
1197 this,
1198 m_currentSlot);
1199 }
1200 else
1201 {
1202 VarTtiAllocInfo allocation = m_currSlotAllocInfo.m_varTtiAllocInfo.front();
1204
1205 Time nextVarTtiStart = GetSymbolPeriod() * allocation.m_dci->m_symStart;
1206
1207 Simulator::Schedule(nextVarTtiStart + m_lastSlotStart - Simulator::Now(),
1208 &NrUePhy::StartVarTti,
1209 this,
1210 allocation.m_dci);
1211 }
1212
1213 m_receptionEnabled = false;
1214}
1215
1216void
1218{
1219 Simulator::ScheduleWithContext(m_netDevice->GetNode()->GetId(),
1222 m_phySapUser,
1223 p);
1224 // m_phySapUser->ReceivePhyPdu (p);
1225}
1226
1227void
1228NrUePhy::SendDataChannels(const Ptr<PacketBurst>& pb,
1229 const std::list<Ptr<NrControlMessage>>& ctrlMsg,
1230 const std::shared_ptr<DciInfoElementTdma>& dci,
1231 const Time& duration)
1232{
1233 if (pb->GetNPackets() > 0)
1234 {
1235 NrRadioBearerTag tag;
1236 if (!pb->GetPackets().front()->PeekPacketTag(tag))
1237 {
1238 NS_FATAL_ERROR("No radio bearer tag");
1239 }
1240 }
1241
1242 m_spectrumPhy->StartTxDataFrames(pb, ctrlMsg, dci, duration);
1243}
1244
1245void
1246NrUePhy::SendCtrlChannels(Time duration)
1247{
1248 m_spectrumPhy->StartTxUlControlFrames(m_ctrlMsgs, duration);
1249 m_ctrlMsgs.clear();
1250}
1251
1252Ptr<NrDlCqiMessage>
1253NrUePhy::CreateDlCqiFeedbackMessage(const SpectrumValue& sinr)
1254{
1255 NS_LOG_FUNCTION(this);
1256 // Create DL CQI CTRL message
1257 Ptr<NrDlCqiMessage> msg = Create<NrDlCqiMessage>();
1258 msg->SetSourceBwp(GetBwpId());
1259 DlCqiInfo dlcqi;
1260
1261 dlcqi.m_rnti = m_rnti;
1262 dlcqi.m_cqiType = DlCqiInfo::WB;
1263
1264 std::vector<int> cqi;
1265 dlcqi.m_wbCqi = m_amc->CreateCqiFeedbackSiso(sinr, dlcqi.m_mcs);
1266 msg->SetDlCqi(dlcqi);
1267
1268 m_cqiFeedbackTrace(m_rnti, dlcqi.m_wbCqi, dlcqi.m_mcs, 1);
1269 return msg;
1270}
1271
1272void
1273NrUePhy::GenerateDlCqiReport(const SpectrumValue& sinr)
1274{
1275 NS_LOG_FUNCTION(this);
1276 // Not totally sure what this is about. We have to check.
1277 if (m_ulConfigured && (m_rnti > 0) && m_receptionEnabled)
1278 {
1279 m_dlDataSinrTrace(GetCellId(), m_rnti, ComputeAvgSinr(sinr), GetBwpId());
1280
1281 if (Simulator::Now() > m_wbCqiLast)
1282 {
1283 Ptr<NrDlCqiMessage> msg = CreateDlCqiFeedbackMessage(sinr);
1284
1285 if (msg)
1286 {
1287 DoSendControlMessage(msg);
1288 }
1289 }
1290 }
1291}
1292
1293void
1295{
1296 NS_LOG_FUNCTION(this);
1297 // get the feedback from NrSpectrumPhy and send it through ideal PUCCH to gNB
1298 Ptr<NrDlHarqFeedbackMessage> msg = Create<NrDlHarqFeedbackMessage>();
1299 msg->SetSourceBwp(GetBwpId());
1300 msg->SetDlHarqFeedback(m);
1301
1302 auto k1It = m_harqIdToK1Map.find(m.m_harqProcessId);
1303
1304 NS_LOG_DEBUG("ReceiveNrDlHarqFeedback"
1305 << " Harq Process " << static_cast<uint32_t>(k1It->first)
1306 << " K1: " << k1It->second << " Frame " << m_currentSlot);
1307
1308 Time event = m_lastSlotStart + (GetSlotPeriod() * k1It->second);
1309 if (event <= Simulator::Now())
1310 {
1311 Simulator::ScheduleNow(&NrUePhy::DoSendControlMessageNow, this, msg);
1312 }
1313 else
1314 {
1315 Simulator::Schedule(event - Simulator::Now(), &NrUePhy::DoSendControlMessageNow, this, msg);
1316 }
1317}
1318
1319void
1320NrUePhy::SetCam(const Ptr<NrChAccessManager>& cam)
1321{
1322 NS_LOG_FUNCTION(this);
1323 NS_ASSERT(cam != nullptr);
1324 m_cam = cam;
1325 m_cam->SetAccessGrantedCallback(
1326 std::bind(&NrUePhy::ChannelAccessGranted, this, std::placeholders::_1));
1327 m_cam->SetAccessDeniedCallback(std::bind(&NrUePhy::ChannelAccessDenied, this));
1328}
1329
1330const SfnSf&
1332{
1333 return m_currentSlot;
1334}
1335
1336uint16_t
1338{
1339 return m_rnti;
1340}
1341
1342void
1343NrUePhy::DoReset()
1344{
1345 NS_LOG_FUNCTION(this);
1346 m_raPreambleId = 255; // value out of range
1347 m_isConnected = false;
1348}
1349
1350void
1351NrUePhy::DoStartCellSearch(uint16_t dlEarfcn)
1352{
1353 NS_LOG_FUNCTION(this << dlEarfcn);
1354 DoSetInitialBandwidth();
1355}
1356
1357void
1358NrUePhy::DoSynchronizeWithGnb(uint16_t cellId, uint16_t dlEarfcn)
1359{
1360 NS_LOG_FUNCTION(this << cellId << dlEarfcn);
1361 DoSynchronizeWithGnb(cellId);
1362}
1363
1364void
1365NrUePhy::DoSetPa(double pa)
1366{
1367 NS_LOG_FUNCTION(this << pa);
1368}
1369
1370void
1371NrUePhy::DoSetRsrpFilterCoefficient(uint8_t rsrpFilterCoefficient)
1372{
1373 NS_LOG_FUNCTION(this << +rsrpFilterCoefficient);
1374}
1375
1376void
1377NrUePhy::DoSynchronizeWithGnb(uint16_t cellId)
1378{
1379 NS_LOG_FUNCTION(this << cellId);
1380 DoSetCellId(cellId);
1381 DoSetInitialBandwidth();
1382}
1383
1384BeamId
1385NrUePhy::GetBeamId([[maybe_unused]] uint16_t rnti) const
1386{
1387 NS_LOG_FUNCTION(this);
1388 // That's a bad specification: the UE PHY doesn't know anything about its beam id.
1389 NS_FATAL_ERROR("ERROR");
1390}
1391
1392void
1393NrUePhy::ScheduleStartEventLoop(uint32_t nodeId, uint16_t frame, uint8_t subframe, uint16_t slot)
1394{
1395 NS_LOG_FUNCTION(this);
1396 Simulator::ScheduleWithContext(nodeId,
1397 MilliSeconds(0),
1398 &NrUePhy::StartEventLoop,
1399 this,
1400 frame,
1401 subframe,
1402 slot);
1403}
1404
1405void
1406NrUePhy::ReportRsReceivedPower(const SpectrumValue& rsReceivedPower)
1407{
1408 NS_LOG_FUNCTION(this << rsReceivedPower);
1409 m_rsrp = 10 * log10(Integral(rsReceivedPower)) + 30;
1410 NS_LOG_DEBUG("RSRP value updated: " << m_rsrp << " dBm");
1411
1412 if (m_enableUplinkPowerControl)
1413 {
1414 m_powerControl->SetLoggingInfo(GetCellId(), m_rnti);
1415 m_powerControl->SetRsrp(m_rsrp);
1416 }
1417}
1418
1419void
1420NrUePhy::ReceivePss(uint16_t cellId, const Ptr<SpectrumValue>& p)
1421{
1422 NS_LOG_FUNCTION(this);
1423
1424 double sum = 0.0;
1425 uint16_t nRB = 0;
1426
1427 uint32_t subcarrierSpacing;
1428 subcarrierSpacing = 15000 * static_cast<uint32_t>(std::pow(2, GetNumerology()));
1429
1430 Values::const_iterator itPi;
1431 for (itPi = p->ConstValuesBegin(); itPi != p->ConstValuesEnd(); itPi++)
1432 {
1433 // convert PSD [W/Hz] to linear power [W] for the single RE
1434 double powerTxW = (*itPi) * subcarrierSpacing;
1435 sum += powerTxW;
1436 nRB++;
1437 }
1438
1439 // measure instantaneous RSRP now (in dBm)
1440 double rsrp = 10 * log10(1000 * (sum / static_cast<double>(nRB)));
1441
1442 NS_LOG_DEBUG("RSRP value updated: " << rsrp << " dBm"
1443 << " for Cell Id: " << cellId << " RNTI: " << m_rnti);
1444
1445 // store RSRP measurements
1446 std::map<uint16_t, UeMeasurementsElement>::iterator itMeasMap =
1447 m_ueMeasurementsMap.find(cellId);
1448 if (itMeasMap == m_ueMeasurementsMap.end())
1449 {
1450 // insert new entry
1451 UeMeasurementsElement newEl;
1452 newEl.rsrpSum = rsrp;
1453 newEl.rsrpNum = 1;
1454 newEl.rsrqSum = 0;
1455 newEl.rsrqNum = 0;
1456
1457 NS_LOG_DEBUG("New RSRP entry for Cell Id: " << cellId << " RNTI: " << m_rnti
1458 << " RSRP: " << newEl.rsrpSum << " dBm"
1459 << " number of entries: " << +newEl.rsrpNum);
1460
1461 m_ueMeasurementsMap.insert(std::pair<uint16_t, UeMeasurementsElement>(cellId, newEl));
1462 }
1463 else
1464 {
1465 (*itMeasMap).second.rsrpSum += rsrp;
1466 (*itMeasMap).second.rsrpNum++;
1467
1468 NS_LOG_DEBUG("Update RSRP entry for Cell Id: "
1469 << cellId << " RNTI: " << m_rnti
1470 << " RSRP Sum: " << (*itMeasMap).second.rsrpSum << " dBm"
1471 << " number of entries: " << +((*itMeasMap).second.rsrpNum));
1472 }
1473}
1474
1475void
1476NrUePhy::ReportUeMeasurements()
1477{
1478 NS_LOG_FUNCTION(this);
1479
1481
1482 std::map<uint16_t, UeMeasurementsElement>::iterator it;
1483 for (it = m_ueMeasurementsMap.begin(); it != m_ueMeasurementsMap.end(); it++)
1484 {
1485 double avg_rsrp;
1486 double avg_rsrq = 0;
1487 if ((*it).second.rsrpNum != 0)
1488 {
1489 avg_rsrp = (*it).second.rsrpSum / static_cast<double>((*it).second.rsrpNum);
1490 }
1491 else
1492 {
1493 NS_LOG_WARN(" RSRP nSamples is zero!");
1494 avg_rsrp = 0;
1495 }
1496
1497 NS_LOG_DEBUG(" Report UE Measurements for CellId "
1498 << (*it).first << " Reporting UE " << m_rnti << " Av. RSRP " << avg_rsrp
1499 << " (nSamples " << +((*it).second.rsrpNum) << ")"
1500 << " BwpID " << GetBwpId());
1501
1502 m_reportRsrpTrace(GetCellId(), m_imsi, m_rnti, avg_rsrp, GetBwpId());
1503
1504 // trigger RLF detection only when UE has an active RRC connection
1505 // and RLF detection attribute is set to true
1506 if (m_isConnected && m_enableRlfDetection)
1507 {
1508 double avrgSinrForRlf = ComputeAvgSinr(m_ctrlSinrForRlf);
1509 RlfDetection(10 * log10(avrgSinrForRlf));
1510 }
1511
1512 NrUeCphySapUser::UeMeasurementsElement newEl;
1513 newEl.m_cellId = (*it).first;
1514 newEl.m_rsrp = avg_rsrp;
1515 newEl.m_rsrq = avg_rsrq; // LEAVE IT 0 FOR THE MOMENT
1516 ret.m_ueMeasurementsList.push_back(newEl);
1518
1519 m_reportUeMeasurements(m_rnti,
1520 (*it).first,
1521 avg_rsrp,
1522 avg_rsrq,
1523 (*it).first == GetCellId(),
1525 }
1526
1527 // report to RRC
1528 m_ueCphySapUser->ReportUeMeasurements(ret);
1529
1530 m_ueMeasurementsMap.clear();
1531 Simulator::Schedule(m_ueMeasurementsFilterPeriod, &NrUePhy::ReportUeMeasurements, this);
1532}
1533
1534void
1535NrUePhy::SetCsiFeedbackType(uint8_t csiFeedbackType)
1536{
1537 m_csiFeedbackType = csiFeedbackType;
1538}
1539
1540void
1541NrUePhy::ReportDlCtrlSinr(const SpectrumValue& sinr)
1542{
1543 NS_LOG_FUNCTION(this);
1544 uint32_t rbUsed = 0;
1545 double sinrSum = 0.0;
1546
1547 for (uint32_t i = 0; i < sinr.GetValuesN(); i++)
1548 {
1549 double currentSinr = sinr.ValuesAt(i);
1550 if (currentSinr != 0)
1551 {
1552 rbUsed++;
1553 sinrSum += currentSinr;
1554 }
1555 }
1556
1557 NS_ASSERT(rbUsed);
1558 m_dlCtrlSinrTrace(GetCellId(), m_rnti, sinrSum / rbUsed, GetBwpId());
1559}
1560
1561uint8_t
1562NrUePhy::ComputeCqi(const SpectrumValue& sinr)
1563{
1564 NS_LOG_FUNCTION(this);
1565 uint8_t mcs; // it is initialized by AMC in the following call
1566 uint8_t wbCqi = m_amc->CreateCqiFeedbackSiso(sinr, mcs);
1567 return wbCqi;
1568}
1569
1570void
1571NrUePhy::StartEventLoop(uint16_t frame, uint8_t subframe, uint16_t slot)
1572{
1573 NS_LOG_FUNCTION(this);
1574
1575 if (GetChannelBandwidth() == 0)
1576 {
1577 NS_LOG_INFO("Initial bandwidth not set, configuring the default one for Cell ID: "
1578 << GetCellId() << ", RNTI: " << GetRnti() << ", BWP ID: " << GetBwpId());
1579 if (GetSubcarrierSpacing() == 0)
1580 {
1581 NS_LOG_INFO("No numerology was set, assuming numerology 0 for Cell ID: "
1582 << GetCellId() << ", RNTI: " << GetRnti() << ", BWP ID: " << GetBwpId());
1583 SetNumerology(0);
1584 }
1585 DoSetInitialBandwidth();
1586 }
1587
1588 NS_LOG_INFO("PHY starting. Configuration: "
1589 << std::endl
1590 << "\t TxPower: " << m_txPower << " dBm" << std::endl
1591 << "\t NoiseFigure: " << m_noiseFigure << std::endl
1592 << "\t TbDecodeLatency: " << GetTbDecodeLatency().GetMicroSeconds() << " us "
1593 << std::endl
1594 << "\t Numerology: " << GetNumerology() << std::endl
1595 << "\t SymbolsPerSlot: " << GetSymbolsPerSlot() << std::endl
1596 << "\t Pattern: " << NrPhy::GetPattern(m_tddPattern) << std::endl
1597 << "Attached to physical channel: " << std::endl
1598 << "\t Channel bandwidth: " << GetChannelBandwidth() << " Hz" << std::endl
1599 << "\t Channel central freq: " << GetCentralFrequency() << " Hz" << std::endl
1600 << "\t Num. RB: " << GetRbNum());
1601 SfnSf startSlot(frame, subframe, slot, GetNumerology());
1602 StartSlot(startSlot);
1603}
1604
1605void
1606NrUePhy::DoSetInitialBandwidth()
1607{
1608 NS_LOG_FUNCTION(this);
1609 // configure initial bandwidth to 6 RBs, numerology 0
1610 double initialBandwidthHz =
1612 // divided by 100*1000 because the parameter should be in 100KHz
1613 uint16_t initialBandwidthIn100KHz = ceil(initialBandwidthHz / (100 * 1000));
1614 // account for overhead that will be reduced when determining real BW
1615 uint16_t initialBandwidthWithOverhead = initialBandwidthIn100KHz / (1 - GetRbOverhead());
1616
1617 NS_ABORT_MSG_IF(initialBandwidthWithOverhead == 0,
1618 " Initial bandwidth could not be set. Parameters provided are: "
1619 "\n dlBandwidthInRBNum = "
1620 << 6 << "\n m_subcarrierSpacing = " << GetSubcarrierSpacing()
1621 << "\n NrSpectrumValueHelper::SUBCARRIERS_PER_RB = "
1623 << "\n m_rbOh = " << GetRbOverhead());
1624
1625 DoSetDlBandwidth(initialBandwidthWithOverhead);
1626}
1627
1628uint16_t
1629NrUePhy::DoGetCellId() const
1630{
1631 return GetCellId();
1632}
1633
1634uint32_t
1635NrUePhy::DoGetDlEarfcn()
1636{
1637 // TBD See how to get rid of this function in future
1638 // Added for the compatibility with 810 MR to LTE.
1639 NS_LOG_FUNCTION(this);
1640 NS_LOG_WARN("DoGetDlEarfcn function is called. This function should be removed in future once "
1641 "NR has its own RRC.");
1642 return 0;
1643}
1644
1645void
1646NrUePhy::DoSetDlBandwidth(uint16_t dlBandwidth)
1647{
1648 NS_LOG_FUNCTION(this << +dlBandwidth);
1649
1650 SetChannelBandwidth(dlBandwidth);
1651
1652 NS_LOG_DEBUG("PHY reconfiguring. Result: "
1653 << std::endl
1654 << "\t TxPower: " << m_txPower << " dBm" << std::endl
1655 << "\t NoiseFigure: " << m_noiseFigure << std::endl
1656 << "\t TbDecodeLatency: " << GetTbDecodeLatency().GetMicroSeconds() << " us "
1657 << std::endl
1658 << "\t Numerology: " << GetNumerology() << std::endl
1659 << "\t SymbolsPerSlot: " << GetSymbolsPerSlot() << std::endl
1660 << "\t Pattern: " << NrPhy::GetPattern(m_tddPattern) << std::endl
1661 << "Attached to physical channel: " << std::endl
1662 << "\t Channel bandwidth: " << GetChannelBandwidth() << " Hz" << std::endl
1663 << "\t Channel central freq: " << GetCentralFrequency() << " Hz" << std::endl
1664 << "\t Num. RB: " << GetRbNum());
1665}
1666
1667void
1668NrUePhy::DoConfigureUplink(uint16_t ulEarfcn, uint8_t ulBandwidth)
1669{
1670 NS_LOG_FUNCTION(this << ulEarfcn << +ulBandwidth);
1671 // Ignore this; should be equal to dlBandwidth
1672 m_ulConfigured = true;
1673}
1674
1675void
1676NrUePhy::DoConfigureReferenceSignalPower(int8_t referenceSignalPower)
1677{
1678 NS_LOG_FUNCTION(this << referenceSignalPower);
1679 m_powerControl->ConfigureReferenceSignalPower(referenceSignalPower);
1680}
1681
1682void
1683NrUePhy::DoSetRnti(uint16_t rnti)
1684{
1685 NS_LOG_FUNCTION(this << rnti);
1686 GetSpectrumPhy()->SetRnti(rnti);
1687 m_rnti = rnti;
1688}
1689
1690void
1691NrUePhy::DoSetTransmissionMode(uint8_t txMode)
1692{
1693 NS_LOG_FUNCTION(this << +txMode);
1694}
1695
1696void
1697NrUePhy::DoSetSrsConfigurationIndex(uint16_t srcCi)
1698{
1699 NS_LOG_FUNCTION(this << srcCi);
1700}
1701
1702void
1704{
1705 m_phySapUser = ptr;
1706}
1707
1708void
1709NrUePhy::DoNotifyConnectionSuccessful()
1710{
1716 if (GetBwpId() == 0)
1717 {
1718 m_isConnected = true;
1719 // Initialize the parameters for radio link failure detection
1720 InitializeRlfParams();
1721 }
1722}
1723
1724void
1725NrUePhy::DoResetPhyAfterRlf()
1726{
1727 NS_LOG_FUNCTION(this);
1728 // m_spectrumPhy->m_harqPhyModule->ClearDlHarqBuffer(m_rnti); // flush HARQ buffers
1729 DoReset();
1730}
1731
1732void
1733NrUePhy::DoResetRlfParams()
1734{
1735 NS_LOG_FUNCTION(this);
1736 InitializeRlfParams();
1737}
1738
1739void
1740NrUePhy::DoStartInSyncDetection()
1741{
1742 NS_LOG_FUNCTION(this);
1743 // indicates that the downlink radio link quality has to be monitored for in-sync indications
1744 m_downlinkInSync = false;
1745}
1746
1747void
1748NrUePhy::InitializeRlfParams()
1749{
1750 NS_LOG_FUNCTION(this);
1751 m_numOfSubframes = 0;
1752 m_sinrDbFrame = 0;
1753 m_numOfFrames = 0;
1754 m_downlinkInSync = true;
1755}
1756
1757void
1758NrUePhy::RlfDetection(double sinrDb)
1759{
1760 NS_LOG_FUNCTION(this << sinrDb);
1761 m_sinrDbFrame += sinrDb;
1762 m_numOfSubframes++;
1763 NS_LOG_LOGIC("No of Subframes: " << m_numOfSubframes
1764 << " UE synchronized: " << m_downlinkInSync);
1765 // check for out_of_sync indications first when UE is both DL and UL synchronized
1766 // m_downlinkInSync=true indicates that the evaluation is for out-of-sync indications
1767 if (m_downlinkInSync && m_numOfSubframes == 10)
1768 {
1773 if ((m_sinrDbFrame / m_numOfSubframes) < m_qOut)
1774 {
1775 m_numOfFrames++; // increment the counter if a frame cannot be decoded
1776 NS_LOG_LOGIC("No of Frames which cannot be decoded: " << m_numOfFrames);
1777 }
1778 else
1779 {
1785 NS_LOG_INFO("Resetting frame counter at phy. Current value = " << m_numOfFrames);
1786 m_numOfFrames = 0;
1787 // Also reset the sync indicator counter at RRC
1788 m_ueCphySapUser->ResetSyncIndicationCounter();
1789 }
1790 m_numOfSubframes = 0;
1791 m_sinrDbFrame = 0;
1792 }
1798 if (m_downlinkInSync && (m_numOfFrames * 10) == m_numOfQoutEvalSf)
1799 {
1800 NS_LOG_LOGIC("At " << Simulator::Now().As(Time::MS)
1801 << " ms UE PHY sending out of sync indication to UE RRC layer");
1802 m_ueCphySapUser->NotifyOutOfSync();
1803 m_numOfFrames = 0;
1804 }
1805 // check for in_sync indications when T310 timer is started
1806 // m_downlinkInSync=false indicates that the evaluation is for in-sync indications
1807 if (!m_downlinkInSync && m_numOfSubframes == 10)
1808 {
1814 if ((m_sinrDbFrame / m_numOfSubframes) > m_qIn)
1815 {
1816 m_numOfFrames++; // increment the counter if a frame can be decoded
1817 NS_LOG_LOGIC("No of Frames successfully decoded: " << m_numOfFrames);
1818 }
1819 else
1820 {
1826 m_numOfFrames = 0;
1827 // Also reset the sync indicator counter at RRC
1828 m_ueCphySapUser->ResetSyncIndicationCounter();
1829 }
1830 m_numOfSubframes = 0;
1831 m_sinrDbFrame = 0;
1832 }
1837 if (!m_downlinkInSync && (m_numOfFrames * 10) == m_numOfQinEvalSf)
1838 {
1839 NS_LOG_LOGIC("At " << Simulator::Now().As(Time::MS)
1840 << " ms UE PHY sending in sync indication to UE RRC layer");
1841 m_ueCphySapUser->NotifyInSync();
1842 m_numOfFrames = 0;
1843 }
1844}
1845
1846void
1847NrUePhy::DoSetImsi(uint64_t imsi)
1848{
1849 NS_LOG_FUNCTION(this);
1850 m_imsi = imsi;
1851}
1852
1853void
1855 NrPmSearch::PmiUpdate pmiUpdateParams)
1856{
1857 NS_LOG_FUNCTION(this);
1858 // Adopted from NrUePhy::GenerateDlCqiReport: CQI feedback requires properly configured UE
1859 if (!m_ulConfigured || (m_rnti == 0))
1860 {
1861 return;
1862 }
1863
1864 // Create DL CQI message for CQI, PMI, and RI. PMI values are updated only if specified by
1865 // pmiUpdateParams, otherwise assume same PMI values as during last CQI feedback
1866 auto cqi = m_pmSearch->CreateCqiFeedbackMimo(rxSignal, pmiUpdateParams);
1867 auto dlcqi = DlCqiInfo{
1868 .m_rnti = m_rnti,
1869 .m_ri = cqi.m_rank,
1870 .m_cqiType = cqi.m_cqiType,
1871 .m_wbCqi = cqi.m_wbCqi,
1872 .m_wbPmi = cqi.m_wbPmi,
1873 .m_sbCqis = cqi.m_sbCqis,
1874 .m_sbPmis = cqi.m_sbPmis,
1875 .m_mcs = cqi.m_mcs,
1876 .m_optPrecMat = cqi.m_optPrecMat,
1877 };
1878
1879 m_cqiFeedbackTrace(m_rnti, cqi.m_wbCqi, cqi.m_mcs, cqi.m_rank);
1880
1881 auto msg = Create<NrDlCqiMessage>();
1882 msg->SetSourceBwp(GetBwpId());
1883 msg->SetDlCqi(dlcqi);
1884
1885 DoSendControlMessage(msg);
1886}
1887
1888uint8_t
1890{
1891 return m_csiFeedbackType;
1892}
1893
1894void
1895NrUePhy::CsiRsReceived(const std::vector<MimoSignalChunk>& csiRsMimoSignal)
1896{
1897 NS_LOG_FUNCTION(this);
1898 NS_ASSERT(csiRsMimoSignal.size() == 1);
1899 m_csiRsMimoSignal = NrMimoSignal(csiRsMimoSignal);
1900 m_lastCsiRsMimoSignalTime = Simulator::Now();
1901}
1902
1903void
1905{
1906 NS_LOG_FUNCTION(this);
1907 NS_ASSERT(m_csiRsMimoSignal.m_chanMat.GetSize() != 0);
1908 NrMimoSignal csiFeedbackSignal = m_csiRsMimoSignal;
1909 // if there is some old interference information use it,
1910 // otherwise, just use the plain CSI-RS signal for the CQI feedback
1911 // (i.e., no interference information). This may happen before any
1912 // PDSCH for this UE is scheduled, and CSI-IM is disabled.
1913 if (m_avgIntCovMat.GetSize() != 0)
1914 {
1915 csiFeedbackSignal.m_covMat = m_avgIntCovMat;
1916 }
1917 TriggerDlCqiGeneration(csiFeedbackSignal, NrPmSearch::PmiUpdate(true, true));
1918}
1919
1920void
1921NrUePhy::CsiImEnded(const std::vector<MimoSignalChunk>& csiImSignalChunks)
1922{
1923 NS_LOG_FUNCTION(this);
1924 // Combine multiple CSI-IM signal chunks into a single channel,
1925 // and interference covariance
1926 auto csiFeedbackSignal = NrMimoSignal(csiImSignalChunks);
1927 CalcAvgIntCovMat(&m_avgIntCovMat, csiFeedbackSignal.m_covMat);
1928 // CSI-IM does not have RX spectrum channel matrix, because it only contains the interference
1929 // hence the channel spectrum matrix to be used is from CSI-RS signal
1930 if (m_alphaCovMat != 1)
1931 {
1932 csiFeedbackSignal.m_covMat = m_csiRsMimoSignal.m_covMat;
1933 }
1934 csiFeedbackSignal.m_chanMat = m_csiRsMimoSignal.m_chanMat;
1935 TriggerDlCqiGeneration(csiFeedbackSignal, NrPmSearch::PmiUpdate(true, true));
1936}
1937
1938void
1939NrUePhy::PdschMimoReceived(const std::vector<MimoSignalChunk>& pdschMimoChunks)
1940{
1941 NS_LOG_FUNCTION(this);
1942 // Combine multiple signal chunks into a single channel matrix and interference covariance
1943 auto csiFeedbackSignal = NrMimoSignal(pdschMimoChunks);
1944 // if alpha != 1, calculate the interference covariance moving average
1945 CalcAvgIntCovMat(&m_avgIntCovMat, csiFeedbackSignal.m_covMat);
1946 if (m_alphaCovMat != 1)
1947 {
1948 csiFeedbackSignal.m_covMat = m_avgIntCovMat;
1949 }
1950 // if CSI-RS enabled, use the spectrum channel matrix from CSI-RS signal
1951 if (m_csiFeedbackType & CQI_CSI_RS)
1952 {
1953 NS_ASSERT_MSG(m_csiRsMimoSignal.m_chanMat.GetSize(),
1954 "CSI-RS based channel matrix not available");
1955 csiFeedbackSignal.m_chanMat = m_csiRsMimoSignal.m_chanMat;
1956 }
1957
1958 // CSI-RS slot, or PDSCH only based CQI feedback
1959 // Determine if an update to wideband or subband PMI is needed and possible
1960 auto pmiUpdateParams = CheckUpdatePmi();
1961 TriggerDlCqiGeneration(csiFeedbackSignal, pmiUpdateParams);
1962}
1963
1964void
1966 NrPmSearch::PmiUpdate pmiUpdateParams)
1967{
1968 NS_LOG_FUNCTION(this);
1969 if (m_pmSearch)
1970 {
1971 GenerateDlCqiReportMimo(csiFeedbackSignal, pmiUpdateParams);
1972 }
1973 else
1974 {
1975 // Interference whitening: normalize the signal such that interference + noise covariance
1976 // matrix is the identity matrix
1977 auto intfNormChanMat =
1978 csiFeedbackSignal.m_covMat.CalcIntfNormChannel(csiFeedbackSignal.m_chanMat);
1979 // Create a dummy precoding matrix
1980 ComplexMatrixArray precMat = ComplexMatrixArray(
1981 csiFeedbackSignal.m_chanMat.GetNumCols(),
1982 csiFeedbackSignal.m_chanMat.GetNumRows(),
1983 csiFeedbackSignal.m_chanMat.GetNumPages(),
1984 std::valarray<std::complex<double>>(std::complex<double>(1.0, 0.0),
1985 csiFeedbackSignal.m_chanMat.GetSize()));
1986 ;
1987
1988 NrSinrMatrix sinrMatrix = intfNormChanMat.ComputeSinrForPrecoding(precMat);
1990 }
1991}
1992
1993void
1994NrUePhy::CalcAvgIntCovMat(NrCovMat* avgIntCovMat, const NrCovMat& newCovMat) const
1995{
1996 NS_LOG_FUNCTION(this);
1997 if (avgIntCovMat->GetSize() == 0)
1998 {
1999 *avgIntCovMat = ComplexMatrixArray(newCovMat.GetNumRows(),
2000 newCovMat.GetNumCols(),
2001 newCovMat.GetNumPages());
2002 };
2003
2004 *avgIntCovMat = newCovMat * std::complex<double>{m_alphaCovMat, 0.0} +
2005 *avgIntCovMat * std::complex<double>{1 - m_alphaCovMat, 0.0};
2006}
2007
2010{
2011 // This implementation only checks if sufficient time has passed since the last update.
2012 // TODO: Improve following logic that defines when to update wideband and/or
2013 // subband PMIs for two-stage codebooks. The algorithm must allow managing the computational
2014 // complexity of PMI updates, and take into account availability of PUCCH/PUSCH resources for
2015 // sending PMI.
2016 auto pmiUpdate = NrPmSearch::PmiUpdate{};
2017 auto now = Simulator::Now();
2018 if (now > m_wbPmiLastUpdate + m_wbPmiUpdateInterval)
2019 {
2020 pmiUpdate.updateWb = true;
2021 m_wbPmiLastUpdate = now;
2022 }
2023 if (now > m_sbPmiLastUpdate + m_sbPmiUpdateInterval)
2024 {
2025 pmiUpdate.updateSb = true;
2026 m_sbPmiLastUpdate = now;
2027 }
2028 return pmiUpdate;
2029}
2030
2031void
2032NrUePhy::SetPmSearch(Ptr<NrPmSearch> pmSearch)
2033{
2034 m_pmSearch = pmSearch;
2035 NS_ASSERT(m_amc);
2036 m_pmSearch->SetAmc(m_amc);
2037}
2038
2039Ptr<NrPmSearch>
2041{
2042 return m_pmSearch;
2043}
2044
2045} // namespace ns3
@ UL_DCI
The resources allocation map from the BS to the attached UEs (UL)
@ DL_HARQ
DL HARQ feedback.
@ MIB
Master Information Block.
@ RAR
Random Access Response.
@ SIB1
System Information Block Type 1.
@ DL_DCI
The resources allocation map from the BS to the attached UEs (DL)
virtual NrIntfNormChanMat CalcIntfNormChannel(const ComplexMatrixArray &chanMat) const
Calculate the interference-normalized channel matrix for SISO and MIMO. See NrIntfNormChanMat for det...
The base class for gNb and UE physical layer.
Definition nr-phy.h:67
Time GetSymbolPeriod() const
Get SymbolPeriod.
Definition nr-phy.cc:858
Ptr< NrSpectrumPhy > m_spectrumPhy
Pointer to the (owned) spectrum phy.
Definition nr-phy.h:558
SlotAllocInfo RetrieveSlotAllocInfo()
Get the head for the slot allocation info, and delete it from the internal list.
Definition nr-phy.cc:788
uint16_t GetCellId() const
Definition nr-phy.cc:657
uint16_t GetNumerology() const
Get the configured numerology.
Definition nr-phy.cc:289
Ptr< NrSpectrumPhy > GetSpectrumPhy() const
Retrieve the SpectrumPhy pointer.
Definition nr-phy.cc:669
void EncodeCtrlMsg(const Ptr< NrControlMessage > &msg)
Take the control messages, and put it in a list that will be sent at the first occasion.
Definition nr-phy.cc:469
void SetNumerology(uint16_t numerology)
Set GNB or UE numerology.
Definition nr-phy.cc:261
virtual void SetTbDecodeLatency(const Time &us)
Configures TB decode latency.
Definition nr-phy.cc:882
std::list< Ptr< NrControlMessage > > m_ctrlMsgs
CTRL messages to be sent.
Definition nr-phy.h:571
void PushBackSlotAllocInfo(const SlotAllocInfo &slotAllocInfo)
Store the slot allocation info.
Definition nr-phy.cc:682
Ptr< PacketBurst > GetPacketBurst(SfnSf sf, uint8_t sym, uint16_t rnti)
Retrieve the PacketBurst at the slot/symbol specified.
Definition nr-phy.cc:364
void SetPowerAllocationType(enum NrSpectrumValueHelper::PowerAllocationType powerAllocationType)
Set power allocation type. There are currently supported two types: one that distributes uniformly en...
Definition nr-phy.cc:432
static std::string GetPattern(const std::vector< LteNrTddSlotType > &pattern)
Get a string representation of a pattern.
Definition nr-phy.cc:413
Time GetSlotPeriod() const
Get the slot period.
Definition nr-phy.cc:321
virtual std::list< Ptr< NrControlMessage > > PopCurrentSlotCtrlMsgs()
Extract and return the message list that is at the beginning of the queue.
Definition nr-phy.cc:610
SlotAllocInfo m_currSlotAllocInfo
Current slot allocation.
Definition nr-phy.h:566
enum NrSpectrumValueHelper::PowerAllocationType GetPowerAllocationType() const
Get the power allocation type.
Definition nr-phy.cc:438
void EnqueueCtrlMsgNow(const Ptr< NrControlMessage > &msg)
Enqueue a CTRL message without considering L1L2CtrlLatency.
Definition nr-phy.cc:452
uint32_t GetChannelBandwidth() const
Retrieve the channel bandwidth, in Hz.
Definition nr-phy.cc:520
virtual Time GetTbDecodeLatency() const
Returns Transport Block decode latency.
Definition nr-phy.cc:888
SlotAllocInfo & PeekSlotAllocInfo(const SfnSf &sfnsf)
Peek the SlotAllocInfo at the SfnSf specified.
Definition nr-phy.cc:817
double GetCentralFrequency() const
Retrieve the frequency (in Hz) of this PHY's channel.
Definition nr-phy.cc:405
double m_noiseFigure
Noise figure (attribute)
Definition nr-phy.h:561
uint32_t m_raPreambleId
Preamble ID.
Definition nr-phy.h:570
double GetNoiseFigure() const
Get the NoiseFigure value.
Definition nr-phy.cc:876
void SetNoiseFigure(double d)
Set the NoiseFigure value.
Definition nr-phy.cc:865
double m_txPower
Transmission power (attribute)
Definition nr-phy.h:560
void InitializeMessageList()
Initialize the message list.
Definition nr-phy.cc:598
Ptr< SpectrumValue > GetTxPowerSpectralDensity(const std::vector< int > &rbIndexVector)
Definition nr-phy.cc:394
void SetChannelBandwidth(uint16_t bandwidth)
Function to set the channel bandwidth, used also by child classes, i.e., see functions DoSetDlBanwidt...
Definition nr-phy.cc:242
uint16_t GetBwpId() const
Definition nr-phy.cc:651
Ptr< NrNetDevice > m_netDevice
Pointer to the owner netDevice.
Definition nr-phy.h:557
bool SlotAllocInfoExists(const SfnSf &sfnsf) const
Check if the SlotAllocationInfo for that slot exists.
Definition nr-phy.cc:773
uint32_t GetSubcarrierSpacing() const
Retrieve the subcarrier spacing in Hz. Subcarrier spacing is updated when the numerology is being upd...
Definition nr-phy.cc:527
uint32_t GetSymbolsPerSlot() const
Get the number of symbols in a slot.
Definition nr-phy.cc:315
std::vector< LteNrTddSlotType > m_tddPattern
Pattern.
Definition nr-phy.h:573
double GetRbOverhead() const
Get the bandwidth overhead used when calculating the usable RB number.
Definition nr-phy.cc:309
uint32_t GetRbNum() const
Get the number of Resource block configured.
Definition nr-phy.cc:514
std::vector< int > FromRBGBitmaskToRBAssignment(const std::vector< bool > rbgBitmask) const
Transform a MAC-made vector of RBG to a PHY-ready vector of SINR indices.
Definition nr-phy.cc:165
void EnqueueCtrlMessage(const Ptr< NrControlMessage > &m)
Enqueue a ctrl message, keeping in consideration L1L2CtrlDelay.
Definition nr-phy.cc:444
void DoDispose() override
DoDispose method inherited from Object.
Definition nr-phy.cc:199
void DoSetCellId(uint16_t cellId)
Set the cell ID.
Definition nr-phy.cc:328
Tag used to define the RNTI and LC id for each MAC packet transmitted.
NrSinrMatrix stores the MIMO SINR matrix, with dimension rank x nRbs.
SpectrumValue GetVectorizedSpecVal() const
Linearize a 2D matrix into a vector, and convert that vector to a SpectrumValue Matches layer-to-code...
static const uint8_t SUBCARRIERS_PER_RB
subcarriers per resource block
virtual void ReportUeMeasurements(UeMeasurementsParameters params)=0
Send a report of RSRP and RSRQ values perceived from PSS by the PHY entity (after applying layer-1 fi...
virtual void ResetSyncIndicationCounter()=0
Reset the sync indication counter.
virtual void RecvSystemInformationBlockType1(uint16_t cellId, NrRrcSap::SystemInformationBlockType1 sib1)=0
Relay an SIB1 message from the PHY entity to the RRC layer.
virtual void RecvMasterInformationBlock(uint16_t cellId, NrRrcSap::MasterInformationBlock mib)=0
Relay an MIB message from the PHY entity to the RRC layer.
virtual void NotifyOutOfSync()=0
Send an out of sync indication to UE RRC.
virtual void NotifyInSync()=0
Send an in sync indication to UE RRC.
void SetUplinkPowerControl(Ptr< NrUePowerControl > pc)
Allow configuration of uplink power control algorithm. E.g. necessary in FDD, when measurements are r...
Definition nr-ue-phy.cc:306
void SetPhySapUser(NrUePhySapUser *ptr)
Install the PHY sap user (AKA the UE MAC)
void ScheduleStartEventLoop(uint32_t nodeId, uint16_t frame, uint8_t subframe, uint16_t slot) override
Start the ue Event Loop.
void ReceivePss(uint16_t cellId, const Ptr< SpectrumValue > &p)
Receive PSS and calculate RSRQ in dBm.
void CalcAvgIntCovMat(NrCovMat *avgIntCovMat, const NrCovMat &newCovMat) const
Calculates the moving average of the interference covariance matrix.
uint8_t GetCsiImDuration() const
Definition nr-ue-phy.cc:274
void EnqueueDlHarqFeedback(const DlHarqInfo &m)
Get the HARQ feedback (on the transmission) from NrSpectrumPhy and send it through ideal PUCCH to gNB...
void SetTxPower(double pow)
Set the transmission power for the UE.
Definition nr-ue-phy.cc:280
void SetSubChannelsForTransmission(const std::vector< int > &mask, uint32_t numSym)
Set the Tx power spectral density based on the RB index vector.
Definition nr-ue-phy.cc:323
NrUePhy()
NrUePhy default constructor.
Definition nr-ue-phy.cc:41
void SetNumRbPerRbg(uint32_t numRbPerRbg)
Function that sets the number of RBs per RBG. This function will be soon deprecated,...
Definition nr-ue-phy.cc:422
BeamId GetBeamId(uint16_t rnti) const override
Get the beam id for the specified user.
NrUePhySapUser * GetPhySapUser() const
Get pointer to PhySapUser.
Definition nr-ue-phy.cc:490
~NrUePhy() override
~NrUePhy
Definition nr-ue-phy.cc:51
Ptr< NrPmSearch > GetPmSearch() const
Get the precoding matrix search engine.
void GenerateCsiRsCqi()
Function that will be called in the case that CSI-RS is received, but CSI-IM is disabled and there is...
void CsiRsReceived(const std::vector< MimoSignalChunk > &csiRsSignal)
A callback function that is called from NrMimoChunkProcessor when CSI-RS is being received....
NrPmSearch::PmiUpdate CheckUpdatePmi()
Check if updates to wideband and/or subband PMI are necessary. This function is used to limit the fre...
void SetPmSearch(Ptr< NrPmSearch > pmSearch)
Set the precoding matrix search engine.
uint32_t GetNumRbPerRbg() const override
Definition nr-ue-phy.cc:466
NrUeCphySapProvider * GetUeCphySapProvider() __attribute__((warn_unused_result))
Retrieve the pointer for the C PHY SAP provider (AKA the PHY interface towards the RRC)
Definition nr-ue-phy.cc:243
Time GetLastSlotStart() const
Get Time of last slot start.
Definition nr-ue-phy.cc:484
uint8_t ComputeCqi(const SpectrumValue &sinr)
Compute the CQI based on the SINR.
void SetAlphaCovMat(double alpha)
Set alpha parameter for the calculation of the CSI interference covariance matrix moving average.
Definition nr-ue-phy.cc:256
void SetDlAmc(const Ptr< const NrAmc > &amc)
Set the AMC pointer from the GNB.
Definition nr-ue-phy.cc:312
double GetTxPower() const override
Retrieve the TX power of the UE.
Definition nr-ue-phy.cc:287
void SetCsiImDuration(uint8_t csiImDuration)
Sets CSI-IM duration in the number of OFDM symbols, if enabled.
Definition nr-ue-phy.cc:268
void SetDlCtrlSyms(uint8_t dlCtrlSyms)
Set the number of DL CTRL symbols.
Definition nr-ue-phy.cc:416
uint8_t GetCsiFeedbackType() const
double GetRsrp() const
Returns the latest measured RSRP value Called by NrUePowerControl.
Definition nr-ue-phy.cc:293
void DoDispose() override
DoDispose method inherited from Object.
Definition nr-ue-phy.cc:57
Ptr< NrUePowerControl > GetUplinkPowerControl() const
Get NR uplink power control entity.
Definition nr-ue-phy.cc:299
void GenerateDlCqiReportMimo(const NrMimoSignal &cqiMimoFeedbackSignal, NrPmSearch::PmiUpdate pmiUpdateParams)
Generate DL CQI, PMI, and RI (channel quality precoding matrix and rank indicators)
void SetCurrentSfnSf(const SfnSf &currentSfnSf)
Set current SfnSf.
Definition nr-ue-phy.cc:472
void GenerateDlCqiReport(const SpectrumValue &sinr)
Generate a DL CQI report.
void SetCam(const Ptr< NrChAccessManager > &cam)
Set the channel access manager interface for this instance of the PHY.
void SetPattern(const std::string &pattern)
Set the UE pattern.
Definition nr-ue-phy.cc:428
void SetUlCtrlSyms(uint8_t ulCtrlSyms)
Set the number of UL CTRL symbols.
Definition nr-ue-phy.cc:410
uint16_t GetRnti() const __attribute__((warn_unused_result))
Get the current RNTI of the user.
void RegisterToGnb(uint16_t bwpId)
Register the UE to a certain Gnb.
Definition nr-ue-phy.cc:401
void FinishSlotProcessing(const SfnSf &s, bool nrAllocationExists)
Finish the StartSlot processing.
Definition nr-ue-phy.cc:851
const SfnSf & GetCurrentSfnSf() const override
Get the current SfnSf.
void SetUeCphySapUser(NrUeCphySapUser *s)
Install ue C PHY SAP user (AKA the PHY interface towards the RRC)
Definition nr-ue-phy.cc:236
void PhyCtrlMessagesReceived(const Ptr< NrControlMessage > &msg)
Receive a list of CTRL messages.
Definition nr-ue-phy.cc:546
double GetAlphaCovMat() const
Definition nr-ue-phy.cc:262
void TriggerDlCqiGeneration(const NrMimoSignal &csiFeedbackSignal, NrPmSearch::PmiUpdate pmiUpdateParams)
Function is called in different possible scenarios to generate CQI information. For example,...
void PdschMimoReceived(const std::vector< MimoSignalChunk > &pdschMimoChunks)
Function is called when PDSCH is received by the UE. It contains the channel and interference informa...
void SetLastSlotStart(Time startTime)
Set last slot start.
Definition nr-ue-phy.cc:478
void ReportDlCtrlSinr(const SpectrumValue &sinr)
Called when DlCtrlSinr is fired.
void PhyDataPacketReceived(const Ptr< Packet > &p)
Receive a PHY data packet.
void ReportRsReceivedPower(const SpectrumValue &power)
Called when rsReceivedPower is fired.
void CsiImEnded(const std::vector< MimoSignalChunk > &csiImSignalChunks)
Function is called when CSI-IM finishes, and this function triggers the update of the interference co...
static TypeId GetTypeId()
Get the object TypeId.
Definition nr-ue-phy.cc:75
SAP interface between the UE PHY and the UE MAC.
Definition nr-phy-sap.h:264
virtual void ReceiveControlMessage(Ptr< NrControlMessage > msg)=0
Receive SendNrControlMessage (PDCCH map, CQI feedbacks) using the ideal control channel.
virtual void ReceivePhyPdu(Ptr< Packet > p)=0
Notify the MAC of the reception of a new PHY-PDU.
virtual void SlotIndication(SfnSf s)=0
Trigger the indication of a new slot for the MAC.
The SfnSf class.
Definition sfnsf.h:32
void Add(uint32_t slotN)
Add to this SfnSf a number of slot indicated by the first parameter.
Definition sfnsf.cc:117
@ 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)
The DlCqiInfo struct.
enum ns3::DlCqiInfo::DlCqiType WB
The type of the CQI.
uint16_t m_rnti
The RNTI.
A struct that contains info for the DL HARQ.
uint8_t m_harqProcessId
ProcessId.
Helper struct for processing and storing received signals for use in CSI feedback.
NrCovMat m_covMat
Interference and noise covariance matrix; nRxPorts * nRxPorts * nRbs.
ComplexMatrixArray m_chanMat
Channel Matrix; nRxPorts * nTxPorts * nRbs.
Parameters that define if PMI should be updated or if previous PMI values are used.
UeMeasurementsParameters structure.
std::vector< UeMeasurementsElement > m_ueMeasurementsList
UE measurement list.
uint8_t m_componentCarrierId
component carrier ID
The SlotAllocInfo struct.
SfnSf m_sfnSf
SfnSf of this allocation.
std::deque< VarTtiAllocInfo > m_varTtiAllocInfo
queue of allocations