5G-LENA nr-v4.0
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-spectrum-phy.cc
1// Copyright (c) 2020 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2//
3// SPDX-License-Identifier: GPL-2.0-only
4
5#include "nr-spectrum-phy.h"
6
7#include "nr-chunk-processor.h"
8#include "nr-gnb-net-device.h"
9#include "nr-gnb-phy.h"
10#include "nr-lte-mi-error-model.h"
11#include "nr-radio-bearer-tag.h"
12#include "nr-ue-net-device.h"
13#include "nr-ue-phy.h"
14
15#include "ns3/boolean.h"
16#include "ns3/double.h"
17#include "ns3/matrix-based-channel-model.h"
18#include "ns3/node.h"
19#include "ns3/trace-source-accessor.h"
20
21#include <numeric>
22
23namespace ns3
24{
25
26NS_LOG_COMPONENT_DEFINE("NrSpectrumPhy");
27NS_OBJECT_ENSURE_REGISTERED(NrSpectrumPhy);
28
29std::ostream&
30operator<<(std::ostream& os, const enum NrSpectrumPhy::State state)
31{
32 switch (state)
33 {
35 os << "TX";
36 break;
38 os << "RX_DL_CTRL";
39 break;
41 os << "RX_UL_CTRL";
42 break;
44 os << "CCA_BUSY";
45 break;
47 os << "RX_DATA";
48 break;
50 os << "IDLE";
51 break;
53 os << "RX_UL_SRS";
54 break;
55 default:
56 NS_ABORT_MSG("Unknown state.");
57 }
58 return os;
59}
60
62 : SpectrumPhy()
63{
64 NS_LOG_FUNCTION(this);
65 m_interferenceData = CreateObject<NrInterference>();
66 m_interferenceCtrl = CreateObject<NrInterference>();
67 m_random = CreateObject<UniformRandomVariable>();
68 m_random->SetAttribute("Min", DoubleValue(0.0));
69 m_random->SetAttribute("Max", DoubleValue(1.0));
70}
71
75
76void
78{
79 NS_LOG_FUNCTION(this);
80 if (m_channel)
81 {
82 m_channel->Dispose();
83 }
84
85 m_channel = nullptr;
86
87 if (m_interferenceData)
88 {
89 m_interferenceData->Dispose();
90 }
91
92 if (m_interferenceCtrl)
93 {
94 m_interferenceCtrl->Dispose();
95 }
96
97 if (m_interferenceSrs)
98 {
99 m_interferenceSrs->Dispose();
100 m_interferenceSrs = nullptr;
101 }
102
103 if (m_interferenceCsiRs)
104 {
105 m_interferenceCsiRs->Dispose();
106 m_interferenceCsiRs = nullptr;
107 }
108
109 if (m_interferenceCsiIm)
110 {
111 m_interferenceCsiIm->Dispose();
112 m_interferenceCsiIm = nullptr;
113 }
114
115 m_interferenceData = nullptr;
116 m_interferenceCtrl = nullptr;
117 m_mobility = nullptr;
118 m_phy = nullptr;
119 m_rxSpectrumModel = nullptr;
120 m_txPsd = nullptr;
121
122 m_phyRxDataEndOkCallback = MakeNullCallback<void, const Ptr<Packet>&>();
123 m_phyDlHarqFeedbackCallback = MakeNullCallback<void, const DlHarqInfo&>();
124 m_phyUlHarqFeedbackCallback = MakeNullCallback<void, const UlHarqInfo&>();
125 m_phyRxPssCallback = MakeNullCallback<void, uint16_t, const Ptr<SpectrumValue>&>();
126
127 SpectrumPhy::DoDispose();
128}
129
130TypeId
132{
133 static TypeId tid =
134 TypeId("ns3::NrSpectrumPhy")
135 .SetParent<SpectrumPhy>()
136 .AddConstructor<NrSpectrumPhy>()
137 .AddAttribute("DataErrorModelEnabled",
138 "Activate/Deactivate the error model of data (TBs of PDSCH and PUSCH) "
139 "[by default is active].",
140 BooleanValue(true),
141 MakeBooleanAccessor(&NrSpectrumPhy::SetDataErrorModelEnabled),
142 MakeBooleanChecker())
143 .AddAttribute("ErrorModelType",
144 "Default type of the Error Model to apply to TBs of PDSCH and PUSCH",
145 TypeIdValue(NrLteMiErrorModel::GetTypeId()),
146 MakeTypeIdAccessor(&NrSpectrumPhy::SetErrorModelType),
147 MakeTypeIdChecker())
148 .AddAttribute(
149 "UnlicensedMode",
150 "Activate/Deactivate unlicensed mode in which energy detection is performed"
151 " and PHY state machine has an additional state CCA_BUSY.",
152 BooleanValue(false),
153 MakeBooleanAccessor(&NrSpectrumPhy::SetUnlicensedMode),
154 MakeBooleanChecker())
155 .AddAttribute("CcaMode1Threshold",
156 "The energy of a received signal should be higher than "
157 "this threshold (dbm) to allow the PHY layer to declare CCA BUSY state.",
158 DoubleValue(-62.0),
159 MakeDoubleAccessor(&NrSpectrumPhy::SetCcaMode1Threshold,
161 MakeDoubleChecker<double>())
162 .AddAttribute(
163 "NumAntennaPanel",
164 "number of panels to install on UE/ gNB device",
165 UintegerValue(1),
167 MakeUintegerChecker<uint8_t>())
168 .AddTraceSource("RxPacketTraceGnb",
169 "The no. of packets received and transmitted by the Base Station",
170 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxPacketTraceGnb),
171 "ns3::RxPacketTraceParams::TracedCallback")
172 .AddTraceSource("TxPacketTraceGnb",
173 "Traces when the packet is being transmitted by the Base Station",
174 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txPacketTraceGnb),
175 "ns3::GnbPhyPacketCountParameter::TracedCallback")
176 .AddTraceSource("RxPacketTraceUe",
177 "The no. of packets received and transmitted by the User Device",
178 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxPacketTraceUe),
179 "ns3::RxPacketTraceParams::TracedCallback")
180 .AddTraceSource(
181 "ChannelOccupied",
182 "This traced callback is triggered every time that the channel is occupied",
183 MakeTraceSourceAccessor(&NrSpectrumPhy::m_channelOccupied),
184 "ns3::Time::TracedCallback")
185 .AddTraceSource("TxDataTrace",
186 "Indicates when the channel is being occupied by a data transmission",
187 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txDataTrace),
188 "ns3::Time::TracedCallback")
189 .AddTraceSource("TxCtrlTrace",
190 "Indicates when the channel is being occupied by a ctrl transmission",
191 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txCtrlTrace),
192 "ns3::Time::TracedCallback")
193 .AddTraceSource("RxDataTrace",
194 "Indicates the reception of data from this cell (reporting the rxPsd "
195 "without interferences)",
196 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxDataTrace),
197 "ns3::RxDataTracedCallback::TracedCallback")
198 .AddTraceSource("DlDataSnrTrace",
199 "Report the SNR computed for each TB in DL",
200 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlDataSnrTrace),
201 "ns3::NrSpectrumPhy::DataSnrTracedCallback")
202 .AddTraceSource("DlCtrlPathloss",
203 "Pathloss calculated for CTRL",
204 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlCtrlPathlossTrace),
205 "ns3::NrSpectrumPhy::DlPathlossTrace")
206 .AddTraceSource("DlDataPathloss",
207 "Pathloss calculated for CTRL",
208 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlDataPathlossTrace),
209 "ns3::NrSpectrumPhy::DlPathlossTrace");
210 return tid;
211}
212
215{
216 return m_state;
217}
218
219Ptr<UniformRandomVariable>
221{
222 return m_random;
223}
224
225Ptr<NrPhy>
226NrSpectrumPhy::GetNrPhy() const
227{
228 return m_phy;
229}
230
231Time
232NrSpectrumPhy::GetFirstRxStart() const
233{
234 return m_firstRxStart;
235}
236
237void
238NrSpectrumPhy::SetFirstRxStart(Time startTime)
239{
240 m_firstRxStart = startTime;
241}
242
243Time
244NrSpectrumPhy::GetFirstRxDuration() const
245{
246 return m_firstRxDuration;
247}
248
249void
250NrSpectrumPhy::SetFirstRxDuration(Time duration)
251{
252 m_firstRxDuration = duration;
253}
254
255void
257{
258 m_activeTransmissions++;
259}
260
261void
263 Ptr<const SpectrumValue> spectrumValue,
264 const Time& duration,
265 uint16_t bwpId,
266 uint16_t cellId) const
267{
268 m_rxDataTrace(sfn, spectrumValue, duration, bwpId, cellId);
269}
270
271void
273{
274 m_txCtrlTrace(duration);
275}
276
277void
279{
280 m_txDataTrace(duration);
281}
282
283// set callbacks
284
285void
287{
288 NS_LOG_FUNCTION(this);
289 m_phyRxDataEndOkCallback = c;
290}
291
292void
294{
295 NS_LOG_FUNCTION(this);
296 m_phyRxCtrlEndOkCallback = c;
297}
298
299void
301{
302 NS_LOG_FUNCTION(this);
303 m_phyRxPssCallback = c;
304}
305
306void
308{
309 NS_LOG_FUNCTION(this);
310 m_phyDlHarqFeedbackCallback = c;
311}
312
313void
315{
316 NS_LOG_FUNCTION(this);
317 m_phyUlHarqFeedbackCallback = c;
318}
319
320// inherited from SpectrumPhy
321void
322NrSpectrumPhy::SetDevice(Ptr<NetDevice> d)
323{
324 NS_LOG_FUNCTION(this << d);
325 m_device = d;
326 // It would be appropriate that the creation of interference for SRS is in the constructor.
327 // But, in the constructor since the device is yet not configured we don't know if we
328 // need or not to create the interference object for SRS. It should be only created at gNBs, and
329 // not at UEs. That is why we postpone the creation to the moment of setting the device.
330 // The other option would be to pass the device as a parameter to the constructor of the
331 // NrSpectrumPhy. But since NrSpectrumPhy inherits this SetDevice function from SpectrumPhy
332 // class, so passing also device as a parameter to constructor would create a more complicate
333 // interface.
334
335 if (m_isGnb)
336 {
337 m_interferenceSrs = CreateObject<NrInterference>();
338 m_interferenceSrs->TraceConnectWithoutContext(
339 "SnrPerProcessedChunk",
340 MakeCallback(&NrSpectrumPhy::UpdateSrsSnrPerceived, this));
341 }
342 else
343 {
344 m_interferenceCsiRs = CreateObject<NrInterference>();
345 m_interferenceCsiIm = CreateObject<NrInterference>();
346 m_interferenceData->TraceConnectWithoutContext(
347 "SnrPerProcessedChunk",
348 MakeCallback(&NrSpectrumPhy::ReportWbDlDataSnrPerceived, this));
349 }
350}
351
352Ptr<NetDevice>
353NrSpectrumPhy::GetDevice() const
354{
355 return m_device;
356}
357
358void
359NrSpectrumPhy::SetMobility(Ptr<MobilityModel> m)
360{
361 NS_LOG_FUNCTION(this << m);
362 m_mobility = m;
363}
364
365Ptr<MobilityModel>
366NrSpectrumPhy::GetMobility() const
367{
368 return m_mobility;
369}
370
371void
372NrSpectrumPhy::SetChannel(Ptr<SpectrumChannel> c)
373{
374 NS_LOG_FUNCTION(this << c);
375 m_channel = c;
376}
377
378Ptr<SpectrumChannel>
380{
381 return m_channel;
382}
383
384Ptr<const SpectrumModel>
385NrSpectrumPhy::GetRxSpectrumModel() const
386{
387 return m_rxSpectrumModel;
388}
389
390Ptr<Object>
392{
393 NS_LOG_FUNCTION(this);
394 NS_ASSERT_MSG(m_numPanels == m_antennaPanels.size(), "mismatch of number of Panels");
395
396 return m_antennaPanels.at(m_activePanelIndex);
397}
398
399Ptr<Object>
400NrSpectrumPhy::GetPanelByIndex(const uint8_t index) const
401{
402 NS_LOG_FUNCTION(this);
403 NS_ASSERT_MSG(m_numPanels == m_antennaPanels.size(), "mismatch of number of Panels");
404 return m_antennaPanels.at(index);
405}
406
407void
408NrSpectrumPhy::SetNumPanels(const uint8_t numPanel)
409{
410 m_numPanels = numPanel;
411}
412
413uint8_t
415{
416 return m_numPanels;
417}
418
419// set/get attributes
420
421void
422NrSpectrumPhy::SetBeamManager(Ptr<BeamManager> b)
423{
424 NS_LOG_FUNCTION(this << b);
425 m_beamManagers.resize(1);
426 m_beamManagers[0] = b;
427}
428
429void
431{
432 NS_LOG_FUNCTION(this << b);
433 m_beamManagers.emplace_back(b);
434}
435
436Ptr<BeamManager>
437NrSpectrumPhy::GetBeamManager()
438{
439 NS_LOG_FUNCTION(this);
440 NS_ASSERT_MSG(m_numPanels == m_antennaPanels.size(), "mismatch of number of Panels");
441 return m_beamManagers.at(m_activePanelIndex);
442}
443
444void
446{
447 NS_LOG_FUNCTION(this);
448 NS_ASSERT_MSG(m_numPanels == m_antennaPanels.size(), "mismatch of number of Panels");
449
450 auto firstPanelBearingAngleRad =
451 (DynamicCast<UniformPlanarArray>(m_antennaPanels[0]))->GetAlpha();
452
453 for (auto i = 0; i < m_numPanels; i++)
454 {
455 m_antennaPanels[i]->GetObject<UniformPlanarArray>()->SetAlpha(
456 CircularBearingAnglesForPanels(firstPanelBearingAngleRad, i));
457 }
458}
459
460void
461NrSpectrumPhy::ConfigPanelsBearingAngles(double firstPanelBearingAngleRad)
462{
463 NS_LOG_FUNCTION(this);
464 NS_ASSERT_MSG(m_numPanels == m_antennaPanels.size(), "mismatch of number of Panels");
465
466 for (auto i = 0; i < m_numPanels; i++)
467 {
468 m_antennaPanels[i]->GetObject<UniformPlanarArray>()->SetAlpha(
469 CircularBearingAnglesForPanels(firstPanelBearingAngleRad, i));
470 }
471}
472
473double
474NrSpectrumPhy::CircularBearingAnglesForPanels(double firstPanelBearingAngleRad,
475 uint8_t panelIndex) const
476{
477 // to cover 360 based on number of ue antenna panels
478 return firstPanelBearingAngleRad + 2 * M_PI * (panelIndex) / m_numPanels;
479}
480
481void
482NrSpectrumPhy::SetErrorModel(Ptr<NrErrorModel> em)
483{
484 NS_LOG_FUNCTION(this << em);
485 m_errorModel = em;
486}
487
488Ptr<NrErrorModel>
489NrSpectrumPhy::GetErrorModel() const
490{
491 return m_errorModel;
492}
493
494void
495NrSpectrumPhy::EnableDlDataPathlossTrace()
496{
497 NS_LOG_FUNCTION(this);
498 m_enableDlDataPathlossTrace = true;
499}
500
501void
502NrSpectrumPhy::EnableDlCtrlPathlossTrace()
503{
504 NS_LOG_FUNCTION(this);
505 m_enableDlCtrlPathlossTrace = true;
506}
507
508void
510{
511 NS_LOG_FUNCTION(this << rnti);
512 m_rnti = rnti;
513 m_hasRnti = true;
514}
515
516uint16_t
518{
519 return m_rnti;
520}
521
522void
524{
525 NS_LOG_FUNCTION(this << thresholdDBm);
526 // convert dBm to Watt
527 m_ccaMode1ThresholdW = (std::pow(10.0, thresholdDBm / 10.0)) / 1000.0;
528}
529
530double
532{
533 // convert Watt to dBm
534 return 10.0 * std::log10(m_ccaMode1ThresholdW * 1000.0);
535}
536
537void
539{
540 NS_LOG_FUNCTION(this << unlicensedMode);
541 m_unlicensedMode = unlicensedMode;
542}
543
544void
546{
547 NS_LOG_FUNCTION(this << dataErrorModelEnabled);
548 m_dataErrorModelEnabled = dataErrorModelEnabled;
549}
550
551void
553{
554 NS_LOG_FUNCTION(this << errorModelType.GetName());
555 m_errorModelType = errorModelType;
556}
557
558// other
559
560void
561NrSpectrumPhy::SetNoisePowerSpectralDensity(const Ptr<const SpectrumValue>& noisePsd)
562{
563 NS_LOG_FUNCTION(this << noisePsd);
564 NS_ASSERT(noisePsd);
565 m_rxSpectrumModel = noisePsd->GetSpectrumModel();
566 m_interferenceData->SetNoisePowerSpectralDensity(noisePsd);
567 m_interferenceCtrl->SetNoisePowerSpectralDensity(noisePsd);
568 if (m_interferenceSrs)
569 {
570 m_interferenceSrs->SetNoisePowerSpectralDensity(noisePsd);
571 }
572 if (m_interferenceCsiRs)
573 {
574 m_interferenceCsiRs->SetNoisePowerSpectralDensity(noisePsd);
575 m_interferenceCsiIm->SetNoisePowerSpectralDensity(noisePsd);
576 }
577}
578
579void
580NrSpectrumPhy::SetTxPowerSpectralDensity(const Ptr<SpectrumValue>& TxPsd)
581{
582 NS_LOG_FUNCTION(this << TxPsd);
583 m_txPsd = TxPsd;
584}
585
586Ptr<const SpectrumValue>
587NrSpectrumPhy::GetTxPowerSpectralDensity()
588{
589 return m_txPsd;
590}
591
592Ptr<MatrixBasedChannelModel::Complex3DVector>
593NrSpectrumPhy::CreateSpectrumChannelMatrix(const Ptr<SpectrumSignalParameters> params) const
594{
595 auto txAntenna = DynamicCast<PhasedArrayModel>(params->txPhy->GetAntenna());
596 auto rxAntenna = DynamicCast<PhasedArrayModel>(GetAntenna());
597 NS_ABORT_MSG_UNLESS(txAntenna && rxAntenna, "Only phased antenna array models are supported.");
598
599 auto txAntennaPorts = txAntenna->GetNumPorts();
600 auto rxAntennaPorts = rxAntenna->GetNumPorts();
601 NS_ASSERT_MSG(txAntennaPorts == 1 && rxAntennaPorts == 1,
602 "The conversion only fits to a single antenna port in Tx and Rx");
603
604 Ptr<const SpectrumValue> rxPsd = params->psd;
605 Ptr<const SpectrumValue> txPsd =
606 DynamicCast<NrSpectrumPhy>(params->txPhy)->GetTxPowerSpectralDensity();
607 uint32_t nRb = rxPsd->GetValuesN();
608 Ptr<MatrixBasedChannelModel::Complex3DVector> channelSpct =
609 Create<MatrixBasedChannelModel::Complex3DVector>(rxAntennaPorts, txAntennaPorts, nRb);
610 auto rxRb = rxPsd->ConstValuesBegin();
611 auto txRb = txPsd->ConstValuesBegin();
612 size_t iRb = 0;
613 while (rxRb != rxPsd->ConstValuesEnd() && txRb != txPsd->ConstValuesEnd())
614 {
615 if (*rxRb != 0.0 && *txRb != 0.0)
616 {
617 auto sqrtvit = sqrt(*rxRb / *txRb);
618 for (size_t u = 0; u < rxAntennaPorts; u++)
619 {
620 for (size_t s = 0; s < txAntennaPorts; s++)
621 {
622 channelSpct->Elem(u, s, iRb) = sqrtvit;
623 }
624 }
625 }
626 rxRb++;
627 txRb++;
628 iRb++;
629 }
630 return channelSpct;
631}
632
633void
634NrSpectrumPhy::StartRx(Ptr<SpectrumSignalParameters> params)
635{
636 NS_LOG_FUNCTION(this);
637 Ptr<const SpectrumValue> rxPsd = params->psd;
638 Time duration = params->duration;
639 NS_LOG_INFO("Start receiving signal: " << params->psd << " duration= " << duration);
640
641 // phased-array mimo expects a channel
642 if (!params->spectrumChannelMatrix &&
643 GetSpectrumChannel()->GetPhasedArraySpectrumPropagationLossModel())
644 {
645 params->spectrumChannelMatrix = CreateSpectrumChannelMatrix(params);
646 }
647
648 // pass it to interference calculations regardless of the type (nr or non-nr)
649 m_interferenceData->AddSignalMimo(params, duration);
650
651 // pass the signal to the interference calculator regardless of the type (nr or non-nr)
652 if (m_interferenceSrs)
653 {
654 m_interferenceSrs->AddSignalMimo(params, duration);
655 }
656
657 Ptr<NrSpectrumSignalParametersDataFrame> nrDataRxParams =
658 DynamicCast<NrSpectrumSignalParametersDataFrame>(params);
659
660 Ptr<NrSpectrumSignalParametersDlCtrlFrame> dlCtrlRxParams =
661 DynamicCast<NrSpectrumSignalParametersDlCtrlFrame>(params);
662
663 Ptr<NrSpectrumSignalParametersUlCtrlFrame> ulCtrlRxParams =
664 DynamicCast<NrSpectrumSignalParametersUlCtrlFrame>(params);
665
666 Ptr<NrSpectrumSignalParametersCsiRs> csiRsRxParams =
667 DynamicCast<NrSpectrumSignalParametersCsiRs>(params);
668
669 if (nrDataRxParams)
670 {
671 if (m_interferenceCsiIm && m_interferenceCsiIm->IsChunkProcessorSet() &&
672 nrDataRxParams->cellId != m_phy->GetCellId())
673 {
674 m_interferenceCsiIm->AddSignalMimo(params, duration);
675 }
676
677 if (nrDataRxParams->cellId == GetCellId())
678 {
679 // Receive only signals intended for this receiver. Receive only
680 // - if the receiver is a UE and the signal's RNTI matches the UE's RNTI,
681 // - or if the receiver device is either a gNB or not configured (has no RNTI)
682 auto isIntendedRx = (nrDataRxParams->rnti == m_rnti) || !m_hasRnti;
683
684 if (isIntendedRx)
685 {
686 StartRxData(nrDataRxParams);
687 }
688 if (!m_isGnb and m_enableDlDataPathlossTrace)
689 {
690 Ptr<const SpectrumValue> txPsd =
691 DynamicCast<NrSpectrumPhy>(nrDataRxParams->txPhy)->GetTxPowerSpectralDensity();
692 Ptr<const SpectrumValue> rxPsd = nrDataRxParams->psd;
693 // this value will be used in EndRxData when ProcessReceivedPacketBurst is called
694 m_dlDataPathloss = 10 * log10(Integral(*txPsd)) - 10 * log10(Integral(*rxPsd));
695 }
696 }
697 else
698 {
699 NS_LOG_INFO(" Received DATA not in sync with this signal (cellId="
700 << nrDataRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
701 }
702 }
703 else if (dlCtrlRxParams != nullptr)
704 {
705 m_interferenceCtrl->AddSignalMimo(params, duration);
706
707 if (!m_isGnb)
708 {
709 if (dlCtrlRxParams->pss)
710 {
711 if (dlCtrlRxParams->cellId == GetCellId())
712 {
713 NS_LOG_DEBUG(
714 "Receiving PSS from Serving Cell with Id: " << dlCtrlRxParams->cellId);
715 }
716 else
717 {
718 NS_LOG_DEBUG(
719 "Receiving PSS from Neighbor Cell with Id: " << dlCtrlRxParams->cellId);
720 }
721
722 if (!m_phyRxPssCallback.IsNull())
723 {
724 m_phyRxPssCallback(dlCtrlRxParams->cellId, dlCtrlRxParams->psd);
725 }
726 }
727
728 if (dlCtrlRxParams->cellId == GetCellId())
729 {
730 m_interferenceCtrl->StartRxMimo(params);
731 StartRxDlCtrl(dlCtrlRxParams);
732
733 if (m_enableDlCtrlPathlossTrace)
734 {
735 Ptr<const SpectrumValue> txPsd =
736 DynamicCast<NrSpectrumPhy>(dlCtrlRxParams->txPhy)
737 ->GetTxPowerSpectralDensity();
738 Ptr<const SpectrumValue> rxPsd = dlCtrlRxParams->psd;
739 double pathloss = 10 * log10(Integral(*txPsd)) - 10 * log10(Integral(*rxPsd));
740 m_dlCtrlPathlossTrace(GetCellId(),
741 GetBwpId(),
742 GetMobility()->GetObject<Node>()->GetId(),
743 pathloss);
744 }
745 }
746 else
747 {
748 NS_LOG_INFO("Received DL CTRL, but not in sync with this signal (cellId="
749 << dlCtrlRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
750 }
751 }
752 else
753 {
754 NS_LOG_DEBUG("DL CTRL ignored at gNB");
755 }
756 }
757 else if (ulCtrlRxParams != nullptr)
758 {
759 if (m_isGnb) // only gNBs should enter into reception of UL CTRL signals
760 {
761 if (ulCtrlRxParams->cellId == GetCellId())
762 {
763 if (IsOnlySrs(ulCtrlRxParams->ctrlMsgList))
764 {
765 StartRxSrs(ulCtrlRxParams);
766 }
767 else
768 {
769 StartRxUlCtrl(ulCtrlRxParams);
770 }
771 }
772 else
773 {
774 NS_LOG_INFO("Received UL CTRL, but not in sync with this signal (cellId="
775 << ulCtrlRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
776 }
777 }
778 else
779 {
780 NS_LOG_DEBUG("UL CTRL ignored at UE device");
781 }
782 }
783 else if (csiRsRxParams != nullptr)
784 {
785 if (m_hasRnti && csiRsRxParams->cellId == GetCellId())
786 {
787 StartRxCsiRs(csiRsRxParams);
788 }
789 }
790 else
791 {
792 NS_LOG_INFO("Received non-nr signal of duration:" << duration);
793 }
794
795 // If in RX or TX state, do not change to CCA_BUSY until is finished
796 // RX or TX state. If in IDLE state, then ok, move to CCA_BUSY if the
797 // channel is found busy.
798 if (m_unlicensedMode && m_state == IDLE)
799 {
800 MaybeCcaBusy();
801 }
802}
803
804void
805NrSpectrumPhy::StartTxDataFrames(const Ptr<PacketBurst>& pb,
806 const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
807 const std::shared_ptr<DciInfoElementTdma> dci,
808 const Time& duration)
809{
810 NS_LOG_FUNCTION(this);
811 switch (m_state)
812 {
813 case RX_DATA:
814 /* no break */
815 [[fallthrough]];
816 case RX_DL_CTRL:
817 /* no break */
818 [[fallthrough]];
819 case RX_UL_CTRL:
820 /* no break*/
821 [[fallthrough]];
822 case RX_UL_SRS:
823 NS_FATAL_ERROR("Cannot TX while RX.");
824 break;
825 case TX:
826 // No break, gNB may transmit multiple times to multiple UEs
827 [[fallthrough]];
828 case CCA_BUSY:
829 NS_LOG_WARN("Start transmitting DATA while in CCA_BUSY state.");
830 /* no break */
831 [[fallthrough]];
832 case IDLE: {
833 NS_ASSERT(m_txPsd);
834
835 ChangeState(TX, duration);
836
837 Ptr<NrSpectrumSignalParametersDataFrame> txParams =
838 Create<NrSpectrumSignalParametersDataFrame>();
839 txParams->duration = duration;
840 txParams->txPhy = this->GetObject<SpectrumPhy>();
841 txParams->psd = m_txPsd;
842 txParams->packetBurst = pb;
843 txParams->cellId = GetCellId();
844 txParams->ctrlMsgList = ctrlMsgList;
845 txParams->rnti = dci->m_rnti;
846 txParams->precodingMatrix = dci->m_precMats;
847
848 /* This section is used for trace */
849 if (m_isGnb)
850 {
852 traceParam.m_noBytes = (txParams->packetBurst) ? txParams->packetBurst->GetSize() : 0;
853 traceParam.m_cellId = txParams->cellId;
854 traceParam.m_isTx = true;
855 traceParam.m_subframeno = 0; // TODO extend this
856
857 m_txPacketTraceGnb(traceParam);
858 }
859
860 m_txDataTrace(duration);
861
862 if (m_channel)
863 {
864 m_channel->StartTx(txParams);
865 }
866 else
867 {
868 NS_LOG_WARN("Working without channel (i.e., under test)");
869 }
870
871 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
872 m_activeTransmissions++;
873 }
874 break;
875 default:
876 NS_LOG_FUNCTION(this << "Programming Error. Code should not reach this point");
877 }
878}
879
880bool
882{
883 return m_state == TX;
884}
885
886void
887NrSpectrumPhy::StartTxDlControlFrames(const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
888 const Time& duration)
889{
890 NS_LOG_FUNCTION(this << duration.As(Time::S));
891 NS_LOG_LOGIC(this << " state: " << m_state);
892
893 switch (m_state)
894 {
895 case RX_DATA:
896 /* no break */
897 case RX_DL_CTRL:
898 /* no break */
899 case RX_UL_CTRL:
900 /* no break*/
901 case RX_UL_SRS:
902 NS_FATAL_ERROR("Cannot TX while RX.");
903 break;
904 case TX:
905 NS_FATAL_ERROR("Cannot TX while already TX.");
906 break;
907 case CCA_BUSY:
908 NS_LOG_WARN("Start transmitting DL CTRL while in CCA_BUSY state.");
909 /* no break */
910 case IDLE: {
911 NS_ASSERT(m_txPsd);
912 ChangeState(TX, duration);
913 Ptr<NrSpectrumSignalParametersDlCtrlFrame> txParams =
914 Create<NrSpectrumSignalParametersDlCtrlFrame>();
915 txParams->duration = duration;
916 txParams->txPhy = GetObject<SpectrumPhy>();
917 txParams->psd = m_txPsd;
918 txParams->cellId = GetCellId();
919 txParams->pss = true;
920 txParams->ctrlMsgList = ctrlMsgList;
921
922 m_txCtrlTrace(duration);
923 if (m_channel)
924 {
925 m_channel->StartTx(txParams);
926 }
927 else
928 {
929 NS_LOG_WARN("Working without channel (i.e., under test)");
930 }
931
932 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
933 m_activeTransmissions++;
934 }
935 }
936}
937
938void
939NrSpectrumPhy::StartTxCsiRs(uint16_t rnti, uint16_t beamId)
940{
941 NS_LOG_LOGIC(this << " state: " << m_state);
942 // we simulate 1ns signals for CSi-RS during DL CTRL duration,
943 // the real overhead is correctly calculated in the TB size
944 Time duration = NanoSeconds(1);
945
946 switch (m_state)
947 {
948 case RX_DATA:
949 /* no break */
950 case RX_DL_CTRL:
951 /* no break */
952 case RX_UL_CTRL:
953 /* no break*/
954 case RX_UL_SRS:
955 NS_FATAL_ERROR("Cannot TX while RX.");
956 break;
957 case TX:
958 NS_FATAL_ERROR("Cannot TX while already TX.");
959 break;
960 case CCA_BUSY:
961 NS_LOG_WARN("Start transmitting CSI-RS while in CCA_BUSY state.");
962 /* no break */
963 case IDLE: {
964 NS_ASSERT(m_txPsd);
965 Ptr<NrSpectrumSignalParametersCsiRs> csiRs = Create<NrSpectrumSignalParametersCsiRs>();
966 csiRs->duration = duration;
967 csiRs->txPhy = GetObject<SpectrumPhy>();
968 csiRs->psd = m_txPsd;
969 csiRs->cellId = GetCellId();
970 csiRs->rnti = rnti;
971 csiRs->beamId = beamId;
972
973 if (m_channel)
974 {
975 NS_LOG_DEBUG("gNB with cellId " << GetCellId()
976 << " transmitting CSI-RS for RNTI:" << csiRs->rnti);
977 m_channel->StartTx(csiRs);
978 }
979 else
980 {
981 NS_LOG_WARN("Working without channel (i.e., under test)");
982 }
983 }
984 }
985}
986
987void
988NrSpectrumPhy::StartTxUlControlFrames(const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
989 const Time& duration)
990{
991 NS_LOG_FUNCTION(this << duration.As(Time::S));
992 NS_LOG_LOGIC(this << " state: " << m_state);
993
994 switch (m_state)
995 {
996 case RX_DATA:
997 /* no break */
998 case RX_DL_CTRL:
999 /* no break */
1000 case RX_UL_CTRL:
1001 /* no break */
1002 case RX_UL_SRS:
1003 NS_FATAL_ERROR("Cannot TX while RX.");
1004 break;
1005 case TX:
1006 NS_FATAL_ERROR("Cannot TX while already TX.");
1007 break;
1008 case CCA_BUSY:
1009 NS_LOG_WARN("Start transmitting UL CTRL while in CCA_BUSY state");
1010 /* no break */
1011 case IDLE: {
1012 NS_ASSERT(m_txPsd);
1013 ChangeState(TX, duration);
1014 Ptr<NrSpectrumSignalParametersUlCtrlFrame> txParams =
1015 Create<NrSpectrumSignalParametersUlCtrlFrame>();
1016 txParams->duration = duration;
1017 txParams->txPhy = GetObject<SpectrumPhy>();
1018 txParams->psd = m_txPsd;
1019 txParams->cellId = GetCellId();
1020 txParams->ctrlMsgList = ctrlMsgList;
1021
1022 m_txCtrlTrace(duration);
1023 if (m_channel)
1024 {
1025 m_channel->StartTx(txParams);
1026 }
1027 else
1028 {
1029 NS_LOG_WARN("Working without channel (i.e., under test)");
1030 }
1031 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
1032 m_activeTransmissions++;
1033 }
1034 }
1035}
1036
1037void
1038NrSpectrumPhy::AddDataPowerChunkProcessor(const Ptr<NrChunkProcessor>& p)
1039{
1040 NS_LOG_FUNCTION(this << p);
1041 m_interferenceData->AddRsPowerChunkProcessor(p);
1042}
1043
1044void
1045NrSpectrumPhy::AddDataSinrChunkProcessor(const Ptr<NrChunkProcessor>& p)
1046{
1047 NS_LOG_FUNCTION(this);
1048 m_interferenceData->AddSinrChunkProcessor(p);
1049}
1050
1051void
1052NrSpectrumPhy::AddSrsSinrChunkProcessor(const Ptr<NrChunkProcessor>& p)
1053{
1054 NS_LOG_FUNCTION(this);
1055 NS_ASSERT_MSG(m_isGnb && m_interferenceSrs,
1056 "SRS interference object does not exist or this device is not gNb so the "
1057 "function should not be called.");
1058 m_interferenceSrs->AddSinrChunkProcessor(p);
1059}
1060
1061void
1062NrSpectrumPhy::ReportDlCtrlSinr(const SpectrumValue& sinr)
1063{
1064 NS_LOG_FUNCTION(this);
1065 Ptr<NrUePhy> phy = (DynamicCast<NrUePhy>(m_phy));
1066 NS_ABORT_MSG_UNLESS(
1067 phy,
1068 "This function should only be called for NrSpectrumPhy belonging to NrUEPhy");
1069 phy->ReportDlCtrlSinr(sinr);
1070}
1071
1072void
1073NrSpectrumPhy::UpdateSrsSinrPerceived(const SpectrumValue& srsSinr)
1074{
1075 NS_LOG_FUNCTION(this << srsSinr);
1076 NS_LOG_INFO("Update SRS SINR perceived with this value: " << srsSinr);
1077
1078 for (auto& srsCallback : m_srsSinrReportCallback)
1079 {
1080 srsCallback(GetCellId(),
1081 m_currentSrsRnti,
1082 Sum(srsSinr) / (srsSinr.GetSpectrumModel()->GetNumBands()));
1083 }
1084}
1085
1086void
1088{
1089 NS_LOG_FUNCTION(this << srsSnr);
1090 NS_LOG_INFO("Update SRS SNR perceived with this value: " << srsSnr);
1091
1092 for (auto& srsSnrCallback : m_srsSnrReportCallback)
1093 {
1094 srsSnrCallback(GetCellId(), m_currentSrsRnti, srsSnr);
1095 }
1096}
1097
1098void
1099NrSpectrumPhy::AddRsPowerChunkProcessor(const Ptr<NrChunkProcessor>& p)
1100{
1101 NS_LOG_FUNCTION(this);
1102 m_interferenceCtrl->AddRsPowerChunkProcessor(p);
1103}
1104
1105void
1106NrSpectrumPhy::AddDlCtrlSinrChunkProcessor(const Ptr<NrChunkProcessor>& p)
1107{
1108 NS_LOG_FUNCTION(this);
1109 m_interferenceCtrl->AddSinrChunkProcessor(p);
1110}
1111
1112void
1113NrSpectrumPhy::UpdateSinrPerceived(const SpectrumValue& sinr)
1114{
1115 NS_LOG_FUNCTION(this << sinr);
1116 NS_LOG_INFO("Update SINR perceived with this value: " << sinr);
1117 m_sinrPerceived = sinr;
1118}
1119
1120void
1121NrSpectrumPhy::InstallPhy(const Ptr<NrPhy>& phyModel)
1122{
1123 m_phy = phyModel;
1124}
1125
1126Ptr<NrPhy>
1127NrSpectrumPhy::GetPhy() const
1128{
1129 return m_phy;
1130}
1131
1132void
1133NrSpectrumPhy::SetAntenna(const Ptr<Object> antenna)
1134{
1135 m_antennaPanels.resize(1);
1136 m_antennaPanels[0] = antenna;
1137}
1138
1139void
1140NrSpectrumPhy::AddPanel(const Ptr<Object> antenna)
1141{
1142 m_antennaPanels.emplace_back(antenna);
1143}
1144
1145void
1146NrSpectrumPhy::SetActivePanel(const uint8_t panelIndex)
1147{
1148 m_activePanelIndex = panelIndex;
1149}
1150
1151Ptr<SpectrumChannel>
1153{
1154 return m_channel;
1155}
1156
1157Ptr<NrInterference>
1159{
1160 NS_LOG_FUNCTION(this);
1161 return m_interferenceData;
1162}
1163
1164void
1166{
1167 NS_LOG_FUNCTION(this);
1168
1169 if (!IsGnb())
1170 {
1171 NS_ASSERT_MSG(m_hasRnti, "Cannot send TB to a UE whose RNTI has not been set");
1172 NS_ASSERT_MSG(m_rnti == expectedTb.m_rnti,
1173 "RNTI of the receiving UE must match the RNTI of the TB");
1174 }
1175
1176 auto it = m_transportBlocks.find(expectedTb.m_rnti);
1177 if (it != m_transportBlocks.end())
1178 {
1179 // might be a TB of an unreceived packet (due to high propagation losses)
1180 m_transportBlocks.erase(it);
1181 }
1182
1183 m_transportBlocks.emplace(expectedTb.m_rnti, expectedTb);
1184 NS_LOG_INFO("Add expected TB for rnti "
1185 << expectedTb.m_rnti << " size=" << expectedTb.m_tbSize
1186 << " mcs=" << static_cast<uint32_t>(expectedTb.m_mcs)
1187 << " symstart=" << static_cast<uint32_t>(expectedTb.m_symStart)
1188 << " numSym=" << static_cast<uint32_t>(expectedTb.m_numSym));
1189}
1190
1191void
1192NrSpectrumPhy::AddExpectedSrsRnti(uint16_t rnti)
1193{
1194 m_currentSrsRnti = rnti;
1195}
1196
1197void
1198NrSpectrumPhy::AddSrsSinrReportCallback(SrsSinrReportCallback callback)
1199{
1200 m_srsSinrReportCallback.push_back(callback);
1201}
1202
1203void
1204NrSpectrumPhy::AddSrsSnrReportCallback(SrsSnrReportCallback callback)
1205{
1206 m_srsSnrReportCallback.push_back(callback);
1207}
1208
1209// private
1210
1211void
1212NrSpectrumPhy::StartRxData(const Ptr<NrSpectrumSignalParametersDataFrame>& params)
1213{
1214 NS_LOG_FUNCTION(this);
1215
1216 m_rxDataTrace(m_phy->GetCurrentSfnSf(),
1217 params->psd,
1218 params->duration,
1219 m_phy->GetBwpId(),
1220 m_phy->GetCellId());
1221
1222 switch (m_state)
1223 {
1224 case TX:
1225 if (m_isGnb) // I am gNB. We are here because some of my rebellious UEs is transmitting
1226 // at the same time as me. -> invalid state.
1227 {
1228 NS_FATAL_ERROR("gNB transmission overlaps in time with UE transmission. CellId:"
1229 << params->cellId);
1230 }
1231 else // I am UE, and while I am transmitting, someone else also transmits. If we are
1232 // transmitting on orthogonal TX PSDs then this is most probably valid situation
1233 // (UEs transmitting to gNB).
1234 {
1235 // Sanity check, that we do not transmit on the same RBs; this sanity check will not
1236 // be the same for sidelink/V2X
1237 NS_ASSERT_MSG((Sum((*m_txPsd) * (*params->psd)) == 0),
1238 "Transmissions overlap in frequency. Their cellId is:" << params->cellId);
1239 return;
1240 }
1241 break;
1242 case RX_DL_CTRL:
1243 /* no break */
1244 case RX_UL_CTRL:
1245 /* no break */
1246 case RX_UL_SRS:
1247 NS_FATAL_ERROR("Cannot receive DATA while receiving CTRL.");
1248 break;
1249 case CCA_BUSY:
1250 NS_LOG_INFO("Start receiving DATA while in CCA_BUSY state.");
1251 /* no break */
1252 case RX_DATA: // RX_DATA while RX_DATA is possible with OFDMA, i.e. gNB receives from
1253 // multiple UEs at the same time
1254 /* no break */
1255 case IDLE: {
1256 m_interferenceData->StartRxMimo(params);
1257
1258 if (m_rxPacketBurstList.empty())
1259 {
1260 NS_ASSERT(m_state == IDLE || m_state == CCA_BUSY);
1261 // first transmission, i.e., we're IDLE and we start RX
1262 m_firstRxStart = Simulator::Now();
1263 m_firstRxDuration = params->duration;
1264 NS_LOG_LOGIC(this << " scheduling EndRx with delay " << params->duration.GetSeconds()
1265 << "s");
1266
1267 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxData, this);
1268 }
1269 else
1270 {
1271 NS_ASSERT(m_state == RX_DATA);
1272 // sanity check: if there are multiple RX events, they
1273 // should occur at the same time and have the same
1274 // duration, otherwise the interference calculation
1275 // won't be correct
1276 NS_ASSERT((m_firstRxStart == Simulator::Now()) &&
1277 (m_firstRxDuration == params->duration));
1278 }
1279
1280 ChangeState(RX_DATA, params->duration);
1281
1282 if (params->packetBurst && !params->packetBurst->GetPackets().empty())
1283 {
1284 m_rxPacketBurstList.push_back(params->packetBurst);
1285 }
1286 // NS_LOG_DEBUG (this << " insert msgs " << params->ctrlMsgList.size ());
1287 m_rxControlMessageList.insert(m_rxControlMessageList.end(),
1288 params->ctrlMsgList.begin(),
1289 params->ctrlMsgList.end());
1290
1291 NS_LOG_LOGIC(this << " numSimultaneousRxEvents = " << m_rxPacketBurstList.size());
1292 }
1293 break;
1294 default:
1295 NS_FATAL_ERROR("Programming Error: Unknown State");
1296 }
1297}
1298
1299void
1300NrSpectrumPhy::StartRxDlCtrl(const Ptr<NrSpectrumSignalParametersDlCtrlFrame>& params)
1301{
1302 // The current code of this function assumes:
1303 // that this function is called only when cellId = m_cellId, which means
1304 // that UE can start to receive DL CTRL only from its own cellId,
1305 // and CTRL from other cellIds will be ignored
1306 NS_LOG_FUNCTION(this);
1307 NS_ASSERT(params->cellId == GetCellId() && !m_isGnb);
1308 // RDF: method currently supports Downlink control only!
1309 switch (m_state)
1310 {
1311 case TX:
1312 NS_FATAL_ERROR("Cannot RX while TX.");
1313 break;
1314 case RX_DATA:
1315 NS_FATAL_ERROR("Cannot RX CTRL while receiving DATA.");
1316 break;
1317 case RX_DL_CTRL:
1318 NS_FATAL_ERROR("Cannot RX DL CTRL while already receiving DL CTRL.");
1319 break;
1320 case RX_UL_CTRL:
1321 /* no break */
1322 case RX_UL_SRS:
1323 NS_FATAL_ERROR("UE should never be in RX_UL_CTRL or RX_UL_SRS state.");
1324 break;
1325 case CCA_BUSY:
1326 NS_LOG_INFO("Start receiving CTRL while channel in CCA_BUSY state.");
1327 /* no break */
1328 case IDLE: {
1329 NS_ASSERT(m_rxControlMessageList.empty());
1330 NS_LOG_LOGIC(this << "receiving DL CTRL from cellId:" << params->cellId
1331 << "and scheduling EndRx with delay " << params->duration);
1332 // store the DCIs
1333 m_rxControlMessageList = params->ctrlMsgList;
1334 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxCtrl, this);
1335 ChangeState(RX_DL_CTRL, params->duration);
1336 break;
1337 }
1338 default: {
1339 NS_FATAL_ERROR("Unknown state.");
1340 break;
1341 }
1342 }
1343}
1344
1345void
1346NrSpectrumPhy::StartRxUlCtrl(const Ptr<NrSpectrumSignalParametersUlCtrlFrame>& params)
1347{
1348 // The current code of this function assumes:
1349 // 1) that this function is called only when cellId = m_cellId
1350 // 2) this function should be only called for gNB, only gNB should enter into reception of
1351 // UL CTRL signals 3) gNB can receive simultaneously signals from various UEs
1352 NS_LOG_FUNCTION(this);
1353 NS_ASSERT(params->cellId == GetCellId() && m_isGnb);
1354 // RDF: method currently supports Uplink control only!
1355 switch (m_state)
1356 {
1357 case TX:
1358 NS_FATAL_ERROR("Cannot RX UL CTRL while TX.");
1359 break;
1360 case RX_DATA:
1361 NS_FATAL_ERROR("Cannot RX UL CTRL while receiving DATA.");
1362 break;
1363 case RX_UL_SRS:
1364 NS_FATAL_ERROR("Cannot start RX UL CTRL while already receiving SRS.");
1365 break;
1366 case RX_DL_CTRL:
1367 NS_FATAL_ERROR("gNB should not be in RX_DL_CTRL state.");
1368 break;
1369 case CCA_BUSY:
1370 NS_LOG_INFO("Start receiving UL CTRL while channel in CCA_BUSY state.");
1371 /* no break */
1372 case RX_UL_CTRL:
1373 /* no break */
1374 case IDLE: {
1375 // at the gNB we can receive more UL CTRL signals simultaneously
1376 if (m_state == IDLE || m_state == CCA_BUSY)
1377 {
1378 // first transmission, i.e., we're IDLE and we start RX
1379 NS_ASSERT(m_rxControlMessageList.empty());
1380 m_firstRxStart = Simulator::Now();
1381 m_firstRxDuration = params->duration;
1382 NS_LOG_LOGIC(this << " scheduling EndRx with delay " << params->duration);
1383 // store the DCIs
1384 m_rxControlMessageList = params->ctrlMsgList;
1385 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxCtrl, this);
1386 ChangeState(RX_UL_CTRL, params->duration);
1387 }
1388 else // already in RX_UL_CTRL state, just add new CTRL messages from other UE
1389 {
1390 NS_ASSERT((m_firstRxStart == Simulator::Now()) &&
1391 (m_firstRxDuration == params->duration));
1392 m_rxControlMessageList.insert(m_rxControlMessageList.end(),
1393 params->ctrlMsgList.begin(),
1394 params->ctrlMsgList.end());
1395 }
1396 break;
1397 }
1398 default: {
1399 NS_FATAL_ERROR("unknown state");
1400 break;
1401 }
1402 }
1403}
1404
1405void
1406NrSpectrumPhy::StartRxSrs(const Ptr<NrSpectrumSignalParametersUlCtrlFrame>& params)
1407{
1408 NS_LOG_FUNCTION(this);
1409 // The current code of this function assumes:
1410 // 1) that this function is called only when cellId = m_cellId
1411 // 2) this function should be only called for gNB, only gNB should enter into reception of
1412 // UL SRS signals 3) SRS should be received only one at a time, otherwise this function
1413 // should assert 4) CTRL message list contains only one message and that one is SRS CTRL
1414 // message
1415 NS_ASSERT(params->cellId == GetCellId() && m_isGnb && m_state != RX_UL_SRS &&
1416 params->ctrlMsgList.size() == 1 &&
1417 (*params->ctrlMsgList.begin())->GetMessageType() == NrControlMessage::SRS);
1418
1419 switch (m_state)
1420 {
1421 case TX:
1422 NS_FATAL_ERROR("Cannot RX SRS while TX.");
1423 break;
1424 case RX_DATA:
1425 NS_FATAL_ERROR("Cannot RX SRS while receiving DATA.");
1426 break;
1427 case RX_DL_CTRL:
1428 NS_FATAL_ERROR("gNB should not be in RX_DL_CTRL state.");
1429 break;
1430 case RX_UL_CTRL:
1431 NS_FATAL_ERROR(
1432 "gNB should not receive simultaneously non SRS and SRS uplink control signals");
1433 break;
1434 case CCA_BUSY:
1435 NS_LOG_INFO("Start receiving UL SRS while channel in CCA_BUSY state.");
1436 /* no break */
1437 case IDLE: {
1438 // at the gNB we can receive only one SRS at a time, and the only allowed states before
1439 // starting it are IDLE or BUSY
1440 m_interferenceSrs->StartRxMimo(params);
1441 // first transmission, i.e., we're IDLE and we start RX, CTRL message list should be
1442 // empty
1443 NS_ASSERT(m_rxControlMessageList.empty());
1444 m_firstRxStart = Simulator::Now();
1445 m_firstRxDuration = params->duration;
1446 NS_LOG_LOGIC(this << " scheduling EndRx for SRS signal reception with delay "
1447 << params->duration);
1448 // store the SRS message in the CTRL message list
1449 m_rxControlMessageList = params->ctrlMsgList;
1450 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxSrs, this);
1451 ChangeState(RX_UL_SRS, params->duration);
1452 }
1453 break;
1454 default: {
1455 // not allowed state for starting the SRS reception
1456 NS_FATAL_ERROR("Not allowed state for starting SRS reception.");
1457 break;
1458 }
1459 }
1460}
1461
1462uint16_t
1464{
1465 return m_phy->GetCellId();
1466}
1467
1468uint16_t
1470{
1471 return m_phy->GetBwpId();
1472}
1473
1474bool
1476{
1477 return m_isGnb;
1478}
1479
1480void
1482{
1483 m_isGnb = isGnb;
1484}
1485
1486void
1487NrSpectrumPhy::ChangeState(State newState, Time duration)
1488{
1489 NS_LOG_LOGIC(this << " change state: " << m_state << " -> " << newState);
1490 m_state = newState;
1491
1492 if (newState == RX_DATA || newState == RX_DL_CTRL || newState == RX_UL_CTRL || newState == TX ||
1493 newState == CCA_BUSY)
1494 {
1495 m_channelOccupied(duration);
1496 }
1497}
1498
1499void
1501{
1502 NS_LOG_FUNCTION(this);
1503
1504 // In case of OFDMA DL, this function will be called multiple times, after each transmission
1505 // to a different UE. In the first call to this function, m_state is changed to IDLE.
1506 NS_ASSERT_MSG(m_state == TX, "In EndTx() but state is not TX; state: " << m_state);
1507 NS_LOG_DEBUG("Number of active transmissions (before decrement): " << m_activeTransmissions);
1508 NS_ASSERT_MSG(m_activeTransmissions, "Ending Tx but no active transmissions");
1509 m_activeTransmissions--;
1510
1511 // change to BUSY or IDLE mode when this is the end of the last transmission
1512 if (m_activeTransmissions == 0)
1513 {
1514 // if in unlicensed mode check after transmission if we are in IDLE or CCA_BUSY mode
1515 if (m_unlicensedMode)
1516 {
1517 MaybeCcaBusy();
1518 }
1519 else
1520 {
1521 ChangeState(IDLE, Seconds(0));
1522 }
1523 }
1524}
1525
1526std::vector<MimoSinrChunk>
1527NrSpectrumPhy::GetMimoSinrForRnti(uint16_t rnti, uint8_t rank)
1528{
1529 // Filter chunks by RNTI of the expected TB. For DL, this step selects only the RX signals
1530 // that were sent towards this UE. For UL, it selects only signals that were sent from the
1531 // UE that is currently being decoded.
1532 std::vector<MimoSinrChunk> res;
1533 for (const auto& chunk : m_mimoSinrPerceived)
1534 {
1535 if (chunk.rnti == rnti)
1536 {
1537 res.emplace_back(chunk);
1538 }
1539 }
1540 if (res.empty())
1541 {
1542 // No received signal found, create all-zero SINR matrix with minimum duration
1543 NS_LOG_WARN("Did not find any SINR matrix matching the current UE's RNTI " << rnti);
1544 auto sinrMat = NrSinrMatrix{rank, m_rxSpectrumModel->GetNumBands()};
1545 auto dur = NanoSeconds(1);
1546 res.emplace_back(MimoSinrChunk{sinrMat, rnti, dur});
1547 }
1548 return res;
1549}
1550
1551void
1552TransportBlockInfo::UpdatePerceivedSinr(const SpectrumValue& perceivedSinr)
1553{
1554 m_sinrAvg = 0.0;
1555 m_sinrMin = 99999999999;
1556 for (const auto& rbIndex : m_expected.m_rbBitmap)
1557 {
1558 m_sinrAvg += perceivedSinr.ValuesAt(rbIndex);
1559 if (perceivedSinr.ValuesAt(rbIndex) < m_sinrMin)
1560 {
1561 m_sinrMin = perceivedSinr.ValuesAt(rbIndex);
1562 }
1563 }
1564
1566
1567 NS_LOG_INFO("Finishing RX, sinrAvg=" << m_sinrAvg << " sinrMin=" << m_sinrMin
1568 << " SinrAvg (dB) " << 10 * log(m_sinrAvg) / log(10));
1569}
1570
1571void
1572NrSpectrumPhy::CheckTransportBlockCorruptionStatus()
1573{
1574 for (auto& tbIt : m_transportBlocks)
1575 {
1576 auto rnti = tbIt.first;
1577 auto& tbInfo = tbIt.second;
1578
1579 tbInfo.UpdatePerceivedSinr(m_sinrPerceived);
1580
1581 if ((!m_dataErrorModelEnabled) || (m_rxPacketBurstList.empty()))
1582 {
1583 continue;
1584 }
1585
1586 const NrErrorModel::NrErrorModelHistory& harqInfoList =
1587 m_harqPhyModule.GetHarqProcessInfoDlUl(tbInfo.m_expected.m_isDownlink,
1588 rnti,
1589 tbInfo.m_expected.m_harqProcessId);
1590
1591 NS_ABORT_MSG_IF(!m_errorModelType.IsChildOf(NrErrorModel::GetTypeId()),
1592 "The error model must be a child of NrErrorModel");
1593
1594 if (!m_errorModel)
1595 {
1596 ObjectFactory emFactory;
1597 emFactory.SetTypeId(m_errorModelType);
1598 m_errorModel = DynamicCast<NrErrorModel>(emFactory.Create());
1599 NS_ABORT_IF(m_errorModel == nullptr);
1600 }
1601
1602 // Output is the output of the error model. From the TBLER we decide
1603 // if the entire TB is corrupted or not
1604
1605 if (!m_mimoSinrPerceived.empty())
1606 {
1607 // The received signal information supports MIMO
1608 const auto& expectedTb = tbInfo.m_expected;
1609 auto sinrChunks = GetMimoSinrForRnti(expectedTb.m_rnti, expectedTb.m_rank);
1610 NS_ASSERT(!sinrChunks.empty());
1611
1612 tbInfo.m_outputOfEM = m_errorModel->GetTbDecodificationStatsMimo(sinrChunks,
1613 expectedTb.m_rbBitmap,
1614 expectedTb.m_tbSize,
1615 expectedTb.m_mcs,
1616 expectedTb.m_rank,
1617 harqInfoList);
1618 }
1619 else
1620 {
1621 // SISO code, required only when there is no NrMimoChunkProcessor
1622 // TODO: change nr-uplink-power-control-test to create a 3gpp channel, and remove this
1623 // code
1624 tbInfo.m_outputOfEM =
1625 m_errorModel->GetTbDecodificationStats(m_sinrPerceived,
1626 tbInfo.m_expected.m_rbBitmap,
1627 tbInfo.m_expected.m_tbSize,
1628 tbInfo.m_expected.m_mcs,
1629 harqInfoList);
1630 }
1631
1632 tbInfo.m_isCorrupted = m_random->GetValue() <= tbInfo.m_outputOfEM->m_tbler;
1633
1634 if (tbInfo.m_isCorrupted)
1635 {
1636 NS_LOG_INFO(
1637 "RNTI " << rnti << " processId " << +tbInfo.m_expected.m_harqProcessId << " size "
1638 << tbInfo.m_expected.m_tbSize << " mcs "
1639 << (uint32_t)tbInfo.m_expected.m_mcs << "rank" << +tbInfo.m_expected.m_rank
1640 << " bitmap " << tbInfo.m_expected.m_rbBitmap.size()
1641 << " rv from MAC: " << +tbInfo.m_expected.m_rv
1642 << " elements in the history: " << harqInfoList.size() << " TBLER "
1643 << tbInfo.m_outputOfEM->m_tbler << " corrupted " << tbInfo.m_isCorrupted);
1644 }
1645 }
1646}
1647
1648void
1649NrSpectrumPhy::SendUlHarqFeedback(uint16_t rnti, TransportBlockInfo& tbInfo)
1650{
1651 // Generate the feedback
1652 UlHarqInfo harqUlInfo;
1653 harqUlInfo.m_rnti = rnti;
1654 harqUlInfo.m_tpc = 0;
1655 harqUlInfo.m_harqProcessId = tbInfo.m_expected.m_harqProcessId;
1656 harqUlInfo.m_numRetx = tbInfo.m_expected.m_rv;
1657 if (tbInfo.m_isCorrupted)
1658 {
1659 harqUlInfo.m_receptionStatus = UlHarqInfo::NotOk;
1660 }
1661 else
1662 {
1663 harqUlInfo.m_receptionStatus = UlHarqInfo::Ok;
1664 }
1665
1666 // Send the feedback
1667 m_phyUlHarqFeedbackCallback(harqUlInfo);
1668
1669 // Arrange the history
1670 if (!tbInfo.m_isCorrupted || tbInfo.m_expected.m_rv == 3)
1671 {
1672 m_harqPhyModule.ResetUlHarqProcessStatus(rnti, tbInfo.m_expected.m_harqProcessId);
1673 }
1674 else
1675 {
1676 m_harqPhyModule.UpdateUlHarqProcessStatus(rnti,
1677 tbInfo.m_expected.m_harqProcessId,
1678 tbInfo.m_outputOfEM);
1679 }
1680}
1681
1682DlHarqInfo
1683NrSpectrumPhy::SendDlHarqFeedback(uint16_t rnti, TransportBlockInfo& tbInfo)
1684{
1685 // Generate the feedback
1686 DlHarqInfo harqDlInfo;
1687 harqDlInfo.m_rnti = rnti;
1688 harqDlInfo.m_harqProcessId = tbInfo.m_expected.m_harqProcessId;
1689 harqDlInfo.m_numRetx = tbInfo.m_expected.m_rv;
1690 harqDlInfo.m_bwpIndex = GetBwpId();
1691 if (tbInfo.m_isCorrupted)
1692 {
1693 harqDlInfo.m_harqStatus = DlHarqInfo::NACK;
1694 }
1695 else
1696 {
1697 harqDlInfo.m_harqStatus = DlHarqInfo::ACK;
1698 }
1699
1700 // Send the feedback
1701 m_phyDlHarqFeedbackCallback(harqDlInfo);
1702
1703 // Arrange the history
1704 if (!tbInfo.m_isCorrupted || tbInfo.m_expected.m_rv == 3)
1705 {
1706 NS_LOG_DEBUG("Reset Dl process: " << +tbInfo.m_expected.m_harqProcessId << " for RNTI "
1707 << rnti);
1708 m_harqPhyModule.ResetDlHarqProcessStatus(rnti, tbInfo.m_expected.m_harqProcessId);
1709 }
1710 else
1711 {
1712 NS_LOG_DEBUG("Update Dl process: " << +tbInfo.m_expected.m_harqProcessId << " for RNTI "
1713 << rnti);
1714 m_harqPhyModule.UpdateDlHarqProcessStatus(rnti,
1715 tbInfo.m_expected.m_harqProcessId,
1716 tbInfo.m_outputOfEM);
1717 }
1718 return harqDlInfo;
1719}
1720
1721void
1722NrSpectrumPhy::ProcessReceivedPacketBurst()
1723{
1724 NS_LOG_FUNCTION(this);
1725 Ptr<NrGnbNetDevice> gnbRx = DynamicCast<NrGnbNetDevice>(GetDevice());
1726 Ptr<NrUeNetDevice> ueRx = DynamicCast<NrUeNetDevice>(GetDevice());
1727 std::map<uint16_t, DlHarqInfo> harqDlInfoMap;
1728 for (auto packetBurst : m_rxPacketBurstList)
1729 {
1730 for (auto packet : packetBurst->GetPackets())
1731 {
1732 if (packet->GetSize() == 0)
1733 {
1734 continue;
1735 }
1736
1737 NrRadioBearerTag bearerTag;
1738 if (!packet->PeekPacketTag(bearerTag))
1739 {
1740 NS_FATAL_ERROR("No radio bearer tag found");
1741 }
1742 uint16_t rnti = bearerTag.GetRnti();
1743
1744 auto itTb = m_transportBlocks.find(rnti);
1745 if (itTb == m_transportBlocks.end())
1746 {
1747 // Packet for other device...
1748 continue;
1749 }
1750 auto& tbInfo = itTb->second;
1751
1752 if (!tbInfo.m_isCorrupted)
1753 {
1754 m_phyRxDataEndOkCallback(packet);
1755 }
1756 else
1757 {
1758 NS_LOG_INFO("TB failed");
1759 }
1760
1761 if (gnbRx)
1762 {
1763 RxPacketTraceParams traceParams(tbInfo,
1764 m_dataErrorModelEnabled,
1765 rnti,
1766 gnbRx->GetCellId(),
1767 GetBwpId(),
1768 255);
1769 m_rxPacketTraceGnb(traceParams);
1770 }
1771 else if (ueRx)
1772 {
1773 Ptr<NrUePhy> phy = (DynamicCast<NrUePhy>(m_phy));
1774 uint8_t cqi = phy->ComputeCqi(m_sinrPerceived);
1775 RxPacketTraceParams traceParams(tbInfo,
1776 m_dataErrorModelEnabled,
1777 rnti,
1778 ueRx->GetTargetGnb()->GetCellId(),
1779 GetBwpId(),
1780 cqi);
1781 m_rxPacketTraceUe(traceParams);
1782
1783 if (m_enableDlDataPathlossTrace)
1784 {
1785 m_dlDataPathlossTrace(GetCellId(),
1786 GetBwpId(),
1787 GetMobility()->GetObject<Node>()->GetId(),
1788 m_dlDataPathloss,
1789 cqi);
1790 }
1791 }
1792
1793 // send HARQ feedback (if not already done for this TB)
1794 if (!tbInfo.m_harqFeedbackSent)
1795 {
1796 tbInfo.m_harqFeedbackSent = true;
1797 if (tbInfo.m_expected.m_isDownlink) // DL TB
1798 {
1799 NS_ASSERT(harqDlInfoMap.find(rnti) == harqDlInfoMap.end());
1800 auto harqDlInfo = SendDlHarqFeedback(rnti, tbInfo);
1801 harqDlInfoMap.insert(std::make_pair(rnti, harqDlInfo));
1802 }
1803 else
1804 {
1805 SendUlHarqFeedback(rnti, tbInfo);
1806 }
1807 }
1808 }
1809 }
1810}
1811
1812void
1813NrSpectrumPhy::EndRxData()
1814{
1815 NS_LOG_FUNCTION(this);
1816 m_interferenceData->EndRx();
1817
1818 NS_ASSERT(m_state == RX_DATA);
1819
1820 // check if transport blocks are corrupted
1821 CheckTransportBlockCorruptionStatus();
1822
1823 // trace packet bursts, then receive non-corrupted and send harq feedback
1824 ProcessReceivedPacketBurst();
1825
1826 // forward control messages of this frame to NrPhy
1827 if (!m_rxControlMessageList.empty() && m_phyRxCtrlEndOkCallback)
1828 {
1829 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1830 }
1831
1832 // if in unlicensed mode check after reception if the state should be
1833 // changed to IDLE or CCA_BUSY
1834 if (m_unlicensedMode)
1835 {
1836 MaybeCcaBusy();
1837 }
1838 else
1839 {
1840 ChangeState(IDLE, Seconds(0));
1841 }
1842
1843 m_rxPacketBurstList.clear();
1844 m_transportBlocks.clear();
1845 m_rxControlMessageList.clear();
1846}
1847
1848void
1849NrSpectrumPhy::EndRxCtrl()
1850{
1851 NS_LOG_FUNCTION(this);
1852 NS_ASSERT(m_state == RX_DL_CTRL || m_state == RX_UL_CTRL);
1853
1854 m_interferenceCtrl->EndRx();
1855
1856 // control error model not supported
1857 // forward control messages of this frame to NrPhy
1858 if (!m_rxControlMessageList.empty())
1859 {
1860 if (m_phyRxCtrlEndOkCallback)
1861 {
1862 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1863 }
1864 }
1865
1866 // if in unlicensed mode check after reception if we are in IDLE or CCA_BUSY mode
1867 if (m_unlicensedMode)
1868 {
1869 MaybeCcaBusy();
1870 }
1871 else
1872 {
1873 ChangeState(IDLE, Seconds(0));
1874 }
1875
1876 m_rxControlMessageList.clear();
1877}
1878
1879void
1880NrSpectrumPhy::EndRxSrs()
1881{
1882 NS_LOG_FUNCTION(this);
1883 NS_ASSERT(m_state == RX_UL_SRS && m_rxControlMessageList.size() == 1);
1884
1885 // notify interference calculator that the reception of SRS is finished,
1886 // so that chunk processors can be notified to calculate SINR, and if other
1887 // processor is registered
1888 m_interferenceSrs->EndRx();
1889
1890 if (m_phyRxCtrlEndOkCallback)
1891 {
1892 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1893 }
1894
1895 // if in unlicensed mode check after reception if we are in IDLE or CCA_BUSY mode
1896 if (m_unlicensedMode)
1897 {
1898 MaybeCcaBusy();
1899 }
1900 else
1901 {
1902 ChangeState(IDLE, Seconds(0));
1903 }
1904
1905 m_rxControlMessageList.clear();
1906}
1907
1908void
1909NrSpectrumPhy::MaybeCcaBusy()
1910{
1911 NS_LOG_FUNCTION(this);
1912 Time delayUntilCcaEnd = m_interferenceData->GetEnergyDuration(m_ccaMode1ThresholdW);
1913 if (!delayUntilCcaEnd.IsZero())
1914 {
1915 NS_LOG_DEBUG("Channel detected BUSY for:" << delayUntilCcaEnd << " ns.");
1916
1917 ChangeState(CCA_BUSY, delayUntilCcaEnd);
1918
1919 // check if with the new energy the channel will be for longer time in CCA_BUSY
1920 if (m_busyTimeEnds < Simulator::Now() + delayUntilCcaEnd)
1921 {
1922 m_busyTimeEnds = Simulator::Now() + delayUntilCcaEnd;
1923
1924 if (m_checkIfIsIdleEvent.IsPending())
1925 {
1926 m_checkIfIsIdleEvent.Cancel();
1927 }
1928
1929 NS_LOG_DEBUG("Check if still BUSY in:" << delayUntilCcaEnd
1930 << " us, and that is at "
1931 " time:"
1932 << Simulator::Now() + delayUntilCcaEnd
1933 << " and current time is:" << Simulator::Now());
1934
1935 m_checkIfIsIdleEvent =
1936 Simulator::Schedule(delayUntilCcaEnd, &NrSpectrumPhy::CheckIfStillBusy, this);
1937 }
1938 }
1939 else
1940 {
1941 NS_ABORT_MSG_IF(m_checkIfIsIdleEvent.IsPending(),
1942 "Unexpected state: returning to IDLE while there is an event "
1943 "running that should switch from CCA_BUSY to IDLE ?!");
1944 NS_LOG_DEBUG("Channel detected IDLE after being in: " << m_state << " state.");
1945 ChangeState(IDLE, Seconds(0));
1946 }
1947}
1948
1949void
1950NrSpectrumPhy::CheckIfStillBusy()
1951{
1952 NS_LOG_FUNCTION(this);
1953 NS_ABORT_MSG_IF(m_state == IDLE, "This function should not be called when in IDLE state.");
1954 // If in state of RX/TX do not switch to CCA_BUSY until RX/TX is finished.
1955 // When RX/TX finishes, check if the channel is still busy.
1956 if (m_state == CCA_BUSY)
1957 {
1958 MaybeCcaBusy();
1959 }
1960 else // RX_DL_CTRL, RX_UL_CTRL, RX_DATA, TX
1961 {
1962 Time delayUntilCcaEnd = m_interferenceData->GetEnergyDuration(m_ccaMode1ThresholdW);
1963
1964 if (delayUntilCcaEnd.IsZero())
1965 {
1966 NS_LOG_INFO(" Channel found IDLE as expected.");
1967 }
1968 else
1969 {
1970 NS_LOG_INFO(" Wait while channel BUSY for: " << delayUntilCcaEnd << " ns.");
1971 }
1972 }
1973}
1974
1975bool
1976NrSpectrumPhy::IsOnlySrs(const std::list<Ptr<NrControlMessage>>& ctrlMsgList)
1977{
1978 NS_ASSERT_MSG(!ctrlMsgList.empty(), "Passed an empty uplink control list");
1979
1980 return ctrlMsgList.size() == 1 &&
1981 (*ctrlMsgList.begin())->GetMessageType() == NrControlMessage::SRS;
1982}
1983
1984void
1986{
1987 NS_LOG_FUNCTION(this << dlDataSnr);
1988
1989 Ptr<NrUeNetDevice> ueNetDevice = DynamicCast<NrUeNetDevice>(GetDevice());
1990
1991 m_dlDataSnrTrace(m_phy->GetCurrentSfnSf(),
1992 m_phy->GetCellId(),
1993 m_phy->GetBwpId(),
1994 ueNetDevice->GetImsi(),
1995 dlDataSnr);
1996}
1997
1998int64_t
2000{
2001 NS_LOG_FUNCTION(this << stream);
2002 m_random->SetStream(stream);
2003 return 1;
2004}
2005
2006void
2007NrSpectrumPhy::UpdateMimoSinrPerceived(const std::vector<MimoSinrChunk>& mimoChunks)
2008{
2009 m_mimoSinrPerceived = mimoChunks;
2010}
2011
2012void
2013NrSpectrumPhy::AddDataMimoChunkProcessor(const Ptr<NrMimoChunkProcessor>& p)
2014{
2015 NS_LOG_FUNCTION(this);
2016 m_interferenceData->AddMimoChunkProcessor(p);
2017}
2018
2019void
2020NrSpectrumPhy::AddCsiRsMimoChunkProcessor(const Ptr<NrMimoChunkProcessor>& p)
2021{
2022 NS_LOG_FUNCTION(this);
2023 m_interferenceCsiRs->AddMimoChunkProcessor(p);
2024}
2025
2026void
2027NrSpectrumPhy::AddCsiImMimoChunkProcessor(const Ptr<NrMimoChunkProcessor>& p)
2028{
2029 NS_LOG_FUNCTION(this);
2030 m_interferenceCsiIm->AddMimoChunkProcessor(p);
2031}
2032
2033void
2034NrSpectrumPhy::StartRxCsiRs(const Ptr<NrSpectrumSignalParametersCsiRs>& csiRsParams)
2035{
2036 // TODO extend in future to support a predefined set of beams
2037 if (csiRsParams->rnti == m_rnti)
2038 {
2039 // add this signal to all the signals
2040 m_interferenceCsiRs->AddSignalMimo(csiRsParams, csiRsParams->duration);
2041 // declare the start of the reception of the CSI-RS signal
2042 m_interferenceCsiRs->StartRxMimo(csiRsParams);
2043 // declare the end of the reception of the CSI-RS signal
2044 Simulator::Schedule(csiRsParams->duration, &NrInterference::EndRx, m_interferenceCsiRs);
2045
2046 Simulator::Schedule(m_ctrlEndTime - Simulator::Now() + NanoSeconds(2),
2047 &NrSpectrumPhy::CheckIfCsiImNeeded,
2048 this,
2049 csiRsParams);
2050 }
2051}
2052
2053void
2054NrSpectrumPhy::CheckIfCsiImNeeded(const Ptr<NrSpectrumSignalParametersCsiRs>& csiRsParams)
2055{
2056 NS_LOG_FUNCTION(this);
2057
2058 Ptr<NrUePhy> nrUePhy = DynamicCast<NrUePhy>(m_phy);
2059 bool pdschCsiEnabled = nrUePhy->GetCsiFeedbackType() & CsiFeedbackFlag::CQI_PDSCH_MIMO;
2060
2061 // CSI-IM enabled
2062 if (m_interferenceCsiIm->IsChunkProcessorSet())
2063 {
2064 // Schedule only CSI-IM if either PDSCH is disabled, or it is enabled but the UE is not
2065 // scheduled in this slot
2066 if ((!pdschCsiEnabled) || (pdschCsiEnabled && !IsUeScheduled()))
2067 {
2068 ScheduleCsiIm(csiRsParams);
2069 }
2070 }
2071 else // CSI-IM not enabled
2072 {
2073 // if CSI-IM and PDSCH are both disable, generate CSI based on CSI-RS
2074 if (!pdschCsiEnabled)
2075 {
2076 nrUePhy->GenerateCsiRsCqi();
2077 }
2078 // else CSI will be generated once PDSCH is being received
2079 }
2080}
2081
2082void
2083NrSpectrumPhy::ScheduleCsiIm(Ptr<SpectrumSignalParameters> csiRsParams) const
2084{
2085 NS_LOG_FUNCTION(this);
2086 NS_ASSERT(m_interferenceCsiIm);
2087 // add fake signal to trigger NrInterference calculation for the duration of the fake CSI-IM
2088 // signal, simulating device wake-up triggered by CSI-RS for interference measurement of channel
2089 Ptr<SpectrumSignalParameters> fakeCsiImSignal = Create<SpectrumSignalParameters>();
2090 fakeCsiImSignal->duration =
2091 m_phy->GetSymbolPeriod() * (DynamicCast<NrUePhy>(m_phy))->GetCsiImDuration();
2092 fakeCsiImSignal->psd = csiRsParams->psd;
2093 fakeCsiImSignal->spectrumChannelMatrix = csiRsParams->spectrumChannelMatrix;
2094 m_interferenceCsiIm->AddSignalMimo(fakeCsiImSignal, fakeCsiImSignal->duration);
2095 m_interferenceCsiIm->StartRxMimo(fakeCsiImSignal);
2096 // schedule NrInterference event when CSI-IM ends to calculate pass the CSI-IM interference
2097 // to the corresponding callback function
2098 Simulator::Schedule(fakeCsiImSignal->duration, &NrInterference::EndRx, m_interferenceCsiIm);
2099}
2100
2101bool
2102NrSpectrumPhy::IsUeScheduled() const
2103{
2104 return m_transportBlocks.contains(m_rnti);
2105}
2106
2107void
2109{
2110 m_ctrlEndTime = ctrlEndTime;
2111}
2112
2113} // namespace ns3
std::vector< Ptr< NrErrorModelOutput > > NrErrorModelHistory
Vector of previous output.
static TypeId GetTypeId()
GetTypeId.
void UpdateDlHarqProcessStatus(uint16_t rnti, uint8_t harqProcId, const Ptr< NrErrorModelOutput > &output)
Update the Info associated to the decodification of an HARQ process for DL (asynchronous)
void ResetUlHarqProcessStatus(uint16_t rnti, uint8_t id)
Reset the info associated to the decodification of an HARQ process for UL (asynchronous)
void UpdateUlHarqProcessStatus(uint16_t rnti, uint8_t harqProcId, const Ptr< NrErrorModelOutput > &output)
Update the Info associated to the decodification of an HARQ process for UL (asynchronous)
void ResetDlHarqProcessStatus(uint16_t rnti, uint8_t id)
Reset the info associated to the decodification of an HARQ process for DL (asynchronous)
const NrErrorModel::NrErrorModelHistory & GetHarqProcessInfoDlUl(bool dl, uint16_t rnti, uint8_t harqProcId)
Return the info of the HARQ procId in case of retransmissions for DL/UL (asynchronous)
Definition nr-harq-phy.h:40
void EndRx() override
static TypeId GetTypeId()
GetTypeId.
double CircularBearingAnglesForPanels(double firstPanelBearingAngleRad, uint8_t panelIndex) const
initialize the bearing angles of panels in to cover 360 Degree
void SetUnlicensedMode(bool unlicensedMode)
Sets whether to perform in unlicensed mode in which the channel monitoring is enabled.
void SetErrorModelType(TypeId errorModelType)
Sets the error model type.
void IncrementActiveTransmissions()
Increase the counter of active transmissions.
void NotifyTxCtrlTrace(Time duration) const
call TxCtrlTrace from subclass
void NotifyRxDataTrace(const SfnSf &sfn, Ptr< const SpectrumValue > spectrumValue, const Time &duration, uint16_t bwpId, uint16_t cellId) const
call RxDataTrace from subclass
Ptr< Object > GetPanelByIndex(const uint8_t index) const
Interface enable to access all panels using proper index.
void AddCsiRsMimoChunkProcessor(const Ptr< NrMimoChunkProcessor > &p)
Connect CSI-RS chunk processor with the corresponding CSI-RS interference object.
void UpdateSrsSnrPerceived(const double srsSnr)
SpectrumPhy that will be called when the SNR for the received SRS at gNB is being calculated.
void ReportWbDlDataSnrPerceived(const double dlDataSnr)
Report wideband perceived downlink data SNR.
void UpdateSinrPerceived(const SpectrumValue &sinr)
SpectrumPhy that will be called when the SINR for the received DATA is being calculated by the interf...
NrSpectrumPhy()
NrSpectrumPhy constructor.
void SetPhyRxCtrlEndOkCallback(const NrPhyRxCtrlEndOkCallback &c)
Sets the callback to be called when CTRL is received successfully.
static TypeId GetTypeId()
Get the object TypeId.
void StartTxUlControlFrames(const std::list< Ptr< NrControlMessage > > &ctrlMsgList, const Time &duration)
Start transmission of UL CTRL.
~NrSpectrumPhy() override
~NrSpectrumPhy
std::function< void(const std::list< Ptr< NrControlMessage > > &, uint8_t)> NrPhyRxCtrlEndOkCallback
This callback method type is used to notify that CTRL is received.
Ptr< UniformRandomVariable > GetErrorModelRv() const
Get pointer to error model random variable.
void AddSrsSnrReportCallback(SrsSnrReportCallback callback)
It adds callback to the list of callbacks that will be notified once SRS is being received.
void AddExpectedTb(ExpectedTb expectedTb)
Instruct the Spectrum Model of a incoming transmission.
void SetPhyDlHarqFeedbackCallback(const NrPhyDlHarqFeedbackCallback &c)
Sets the callback to be called when DL HARQ feedback is generated.
void AddBeamManager(Ptr< BeamManager > b)
Adds the beam manager of corresponds spectrum phy of antenna panel, and that beam manager is responsi...
void AddDataPowerChunkProcessor(const Ptr< NrChunkProcessor > &p)
Adds the chunk processor that will process the power for the data.
Ptr< NrInterference > GetNrInterference() const
void SetNumPanels(const uint8_t numPanel)
Set the number of panels in this NrSpectrumPhy.
uint8_t GetNumPanels() const
Get the number of panels in this NrSpectrumPhy.
void DoDispose() override
DoDispose method inherited from Object.
void UpdateSrsSinrPerceived(const SpectrumValue &srsSinr)
SpectrumPhy that will be called when the SINR for the received SRS at gNB is being calculated by the ...
Callback< void, const UlHarqInfo & > NrPhyUlHarqFeedbackCallback
virtual void SetNoisePowerSpectralDensity(const Ptr< const SpectrumValue > &noisePsd)
Sets noise power spectral density to be used by this device.
void SetCcaMode1Threshold(double thresholdDBm)
Set clear channel assessment (CCA) threshold.
void StartTxDlControlFrames(const std::list< Ptr< NrControlMessage > > &ctrlMsgList, const Time &duration)
Starts transmission of DL CTRL.
void InstallPhy(const Ptr< NrPhy > &phyModel)
Set NrPhy of this spectrum phy in order to be able to obtain information such as cellId,...
void SetRnti(uint16_t rnti)
void UpdateMimoSinrPerceived(const std::vector< MimoSinrChunk > &sinr)
Store the SINR chunks for all received signals at end of interference calculations.
bool IsTransmitting()
Return true if the current Phy State is TX.
void AddPanel(const Ptr< Object > antenna)
Add the antenna panel to this NrSpectrumPhy, currently in NR module it is expected to be of type Unif...
double GetCcaMode1Threshold() const
Callback< void, const DlHarqInfo & > NrPhyDlHarqFeedbackCallback
void AddCsiImMimoChunkProcessor(const Ptr< NrMimoChunkProcessor > &p)
Connect CSI-IM chunk processor with the corresponding CSI-IM interference object.
State GetState() const
Get current state.
void SetIsGnb(bool isGnb)
Set whether this spectrum PHY belongs to Gnb or UE TODO NrHelper should be declared as friend and thi...
void EndTx()
Function that is called when the transmission has ended. It is used to update spectrum phy state.
void AddDlCtrlSinrChunkProcessor(const Ptr< NrChunkProcessor > &p)
Adds the chunk processor that will process the received power.
Ptr< SpectrumChannel > GetChannel() const
Get pointer to SpectrumChannel.
int64_t AssignStreams(int64_t stream)
Ptr< SpectrumChannel > GetSpectrumChannel() const
Returns spectrum channel object to which is attached this spectrum phy instance.
void ConfigPanelsBearingAngles()
Either initialize the bearing angles of panels in install step or update all bearing angles based on ...
uint16_t GetCellId() const
void StartTxCsiRs(uint16_t rnti, uint16_t beamId)
Callback< void, uint16_t, const Ptr< SpectrumValue > & > NrPhyRxPssCallback
void SetDataErrorModelEnabled(bool dataErrorModelEnabled)
Enables or disabled data error model.
void SetPhyRxDataEndOkCallback(const NrPhyRxDataEndOkCallback &c)
Sets the callback to be called when DATA is received successfully.
void ReportDlCtrlSinr(const SpectrumValue &sinr)
Called when DlCtrlSinr is fired.
void AddExpectedDlCtrlEnd(Time ctrlEndTime)
Keeps track of when DL CTRL should finish. Needed for CSI-RS and CSI-IM implementation to be able to ...
void SetTxPowerSpectralDensity(const Ptr< SpectrumValue > &txPsd)
Sets transmit power spectral density.
void SetPhyUlHarqFeedbackCallback(const NrPhyUlHarqFeedbackCallback &c)
Sets the callback to be called when UL HARQ feedback is generated.
void SetAntenna(Ptr< Object > antenna)
Sets the antenna of this NrSpectrumPhy instance, currently in NR module it is expected to be of type ...
void AddDataSinrChunkProcessor(const Ptr< NrChunkProcessor > &p)
Adds the chunk processor that will process the interference.
void AddSrsSinrReportCallback(SrsSinrReportCallback callback)
It adds callback to the list of callbacks that will be notified once SRS is being received.
void StartTxDataFrames(const Ptr< PacketBurst > &pb, const std::list< Ptr< NrControlMessage > > &ctrlMsgList, const std::shared_ptr< DciInfoElementTdma > dci, const Time &duration)
Starts transmission of data frames on connected spectrum channel object.
void AddDataMimoChunkProcessor(const Ptr< NrMimoChunkProcessor > &p)
Connect DATA chunk processor with the corresponding DATA interference object.
Callback< void, const Ptr< Packet > & > NrPhyRxDataEndOkCallback
This callback method type is used to notify that DATA is received.
State
Enum that defines possible states of the spectrum phy.
@ RX_DATA
Receiving data.
@ TX
Transmitting state (data or ctrl)
@ RX_UL_SRS
Receiving SRS.
@ CCA_BUSY
BUSY state (channel occupied by another entity)
@ RX_UL_CTRL
Receiving UL CTRL.
@ RX_DL_CTRL
Receiving DL CTRL.
@ IDLE
IDLE state (no action in progress)
void AddRsPowerChunkProcessor(const Ptr< NrChunkProcessor > &p)
Adds the chunk processor that will process the received power.
void SetActivePanel(const uint8_t panelIndex)
Set the active antenna panel to this NrSpectrumPhy,.
uint16_t GetBwpId() const
void StartRx(Ptr< SpectrumSignalParameters > params) override
Inherited from SpectrumPhy. When this function is called this spectrum phy starts receiving a signal ...
void ChangeState(State newState, Time duration)
Update the state of the spectrum phy. The states are: IDLE, TX, RX_DATA, RX_DL_CTRL,...
void NotifyTxDataTrace(Time duration) const
call TxDataTrace from subclass
Ptr< Object > GetAntenna() const override
Inherited from SpectrumPhy Note: Implements GetRxAntenna function from SpectrumPhy.
void SetPhyRxPssCallback(const NrPhyRxPssCallback &c)
uint16_t GetRnti() const
The SfnSf class.
Definition sfnsf.h:32
enum ns3::DlHarqInfo::HarqStatus NACK
HARQ status.
Information about the expected transport block at a certain point in the slot.
uint16_t m_rnti
RNTI.
uint8_t m_symStart
Sym start.
std::vector< int > m_rbBitmap
RB Bitmap.
uint8_t m_numSym
Num sym.
uint32_t m_tbSize
TBSize.
The GnbPhyPacketCountParameter struct.
double m_sinrMin
MIN SINR (only between the RB used to transmit the TB)
void UpdatePerceivedSinr(const SpectrumValue &perceivedSinr)
Update minimum and average SINR of the transport block based on perceived SINR.
ExpectedTb m_expected
Expected data from the PHY. Filled by AddExpectedTb.
double m_sinrAvg
AVG SINR (only for the RB used to transmit the TB)