5G-LENA nr-v3.0-32-g83aee33
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-spectrum-phy.cc
1/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil; -*- */
2
3// Copyright (c) 2020 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
4//
5// SPDX-License-Identifier: GPL-2.0-only
6
7#include "nr-spectrum-phy.h"
8
9#include "nr-gnb-net-device.h"
10#include "nr-gnb-phy.h"
11#include "nr-lte-mi-error-model.h"
12#include "nr-ue-net-device.h"
13#include "nr-ue-phy.h"
14
15#include "ns3/node.h"
16#include "ns3/uniform-planar-array.h"
17#include <ns3/boolean.h>
18#include <ns3/double.h>
19#include <ns3/lte-radio-bearer-tag.h>
20#include <ns3/trace-source-accessor.h>
21
22namespace ns3
23{
24
25NS_LOG_COMPONENT_DEFINE("NrSpectrumPhy");
26NS_OBJECT_ENSURE_REGISTERED(NrSpectrumPhy);
27
28std::ostream&
29operator<<(std::ostream& os, const enum NrSpectrumPhy::State state)
30{
31 switch (state)
32 {
34 os << "TX";
35 break;
37 os << "RX_DL_CTRL";
38 break;
40 os << "RX_UL_CTRL";
41 break;
43 os << "CCA_BUSY";
44 break;
46 os << "RX_DATA";
47 break;
49 os << "IDLE";
50 break;
52 os << "RX_UL_SRS";
53 break;
54 default:
55 NS_ABORT_MSG("Unknown state.");
56 }
57 return os;
58}
59
61 : SpectrumPhy()
62{
63 m_interferenceData = CreateObject<NrInterference>();
64 m_interferenceCtrl = CreateObject<NrInterference>();
65 m_random = CreateObject<UniformRandomVariable>();
66 m_random->SetAttribute("Min", DoubleValue(0.0));
67 m_random->SetAttribute("Max", DoubleValue(1.0));
68}
69
73
74void
76{
77 NS_LOG_FUNCTION(this);
78 if (m_channel)
79 {
80 m_channel->Dispose();
81 }
82
83 m_channel = nullptr;
84
85 if (m_interferenceData)
86 {
87 m_interferenceData->Dispose();
88 }
89
90 if (m_interferenceCtrl)
91 {
92 m_interferenceCtrl->Dispose();
93 }
94
95 if (m_interferenceSrs)
96 {
97 m_interferenceSrs->Dispose();
98 m_interferenceSrs = nullptr;
99 }
100
101 m_interferenceData = nullptr;
102 m_interferenceCtrl = nullptr;
103 m_mobility = nullptr;
104 m_phy = nullptr;
105
106 m_phyRxDataEndOkCallback = MakeNullCallback<void, const Ptr<Packet>&>();
107 m_phyDlHarqFeedbackCallback = MakeNullCallback<void, const DlHarqInfo&>();
108 m_phyUlHarqFeedbackCallback = MakeNullCallback<void, const UlHarqInfo&>();
109
110 SpectrumPhy::DoDispose();
111}
112
113TypeId
115{
116 static TypeId tid =
117 TypeId("ns3::NrSpectrumPhy")
118 .SetParent<SpectrumPhy>()
119 .AddConstructor<NrSpectrumPhy>()
120 .AddAttribute("DataErrorModelEnabled",
121 "Activate/Deactivate the error model of data (TBs of PDSCH and PUSCH) "
122 "[by default is active].",
123 BooleanValue(true),
124 MakeBooleanAccessor(&NrSpectrumPhy::SetDataErrorModelEnabled),
125 MakeBooleanChecker())
126 .AddAttribute("ErrorModelType",
127 "Default type of the Error Model to apply to TBs of PDSCH and PUSCH",
128 TypeIdValue(NrLteMiErrorModel::GetTypeId()),
129 MakeTypeIdAccessor(&NrSpectrumPhy::SetErrorModelType),
130 MakeTypeIdChecker())
131 .AddAttribute(
132 "UnlicensedMode",
133 "Activate/Deactivate unlicensed mode in which energy detection is performed"
134 " and PHY state machine has an additional state CCA_BUSY.",
135 BooleanValue(false),
136 MakeBooleanAccessor(&NrSpectrumPhy::SetUnlicensedMode),
137 MakeBooleanChecker())
138 .AddAttribute("CcaMode1Threshold",
139 "The energy of a received signal should be higher than "
140 "this threshold (dbm) to allow the PHY layer to declare CCA BUSY state.",
141 DoubleValue(-62.0),
142 MakeDoubleAccessor(&NrSpectrumPhy::SetCcaMode1Threshold,
144 MakeDoubleChecker<double>())
145 .AddTraceSource("RxPacketTraceEnb",
146 "The no. of packets received and transmitted by the Base Station",
147 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxPacketTraceEnb),
148 "ns3::RxPacketTraceParams::TracedCallback")
149 .AddTraceSource("TxPacketTraceEnb",
150 "Traces when the packet is being transmitted by the Base Station",
151 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txPacketTraceEnb),
152 "ns3::GnbPhyPacketCountParameter::TracedCallback")
153 .AddTraceSource("RxPacketTraceUe",
154 "The no. of packets received and transmitted by the User Device",
155 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxPacketTraceUe),
156 "ns3::RxPacketTraceParams::TracedCallback")
157 .AddTraceSource(
158 "ChannelOccupied",
159 "This traced callback is triggered every time that the channel is occupied",
160 MakeTraceSourceAccessor(&NrSpectrumPhy::m_channelOccupied),
161 "ns3::Time::TracedCallback")
162 .AddTraceSource("TxDataTrace",
163 "Indicates when the channel is being occupied by a data transmission",
164 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txDataTrace),
165 "ns3::Time::TracedCallback")
166 .AddTraceSource("TxCtrlTrace",
167 "Indicates when the channel is being occupied by a ctrl transmission",
168 MakeTraceSourceAccessor(&NrSpectrumPhy::m_txCtrlTrace),
169 "ns3::Time::TracedCallback")
170 .AddTraceSource("RxDataTrace",
171 "Indicates the reception of data from this cell (reporting the rxPsd "
172 "without interferences)",
173 MakeTraceSourceAccessor(&NrSpectrumPhy::m_rxDataTrace),
174 "ns3::RxDataTracedCallback::TracedCallback")
175 .AddTraceSource("DlDataSnrTrace",
176 "Report the SNR computed for each TB in DL",
177 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlDataSnrTrace),
178 "ns3::NrSpectrumPhy::DataSnrTracedCallback")
179 .AddTraceSource("DlCtrlPathloss",
180 "Pathloss calculated for CTRL",
181 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlCtrlPathlossTrace),
182 "ns3::NrSpectrumPhy::DlPathlossTrace")
183 .AddTraceSource("DlDataPathloss",
184 "Pathloss calculated for CTRL",
185 MakeTraceSourceAccessor(&NrSpectrumPhy::m_dlDataPathlossTrace),
186 "ns3::NrSpectrumPhy::DlPathlossTrace");
187
188 return tid;
189}
190
191// set callbacks
192
193void
195{
196 NS_LOG_FUNCTION(this);
197 m_phyRxDataEndOkCallback = c;
198}
199
200void
202{
203 NS_LOG_FUNCTION(this);
204 m_phyRxCtrlEndOkCallback = c;
205}
206
207void
209{
210 NS_LOG_FUNCTION(this);
211 m_phyRxPssCallback = c;
212}
213
214void
216{
217 NS_LOG_FUNCTION(this);
218 m_phyDlHarqFeedbackCallback = c;
219}
220
221void
223{
224 NS_LOG_FUNCTION(this);
225 m_phyUlHarqFeedbackCallback = c;
226}
227
228// inherited from SpectrumPhy
229void
230NrSpectrumPhy::SetDevice(Ptr<NetDevice> d)
231{
232 m_device = d;
233 // It would be appropriate that the creation of interference for SRS is in the constructor.
234 // But, in the constructor since the device is yet not configured we don't know if we
235 // need or not to create the interference object for SRS. It should be only created at gNBs, and
236 // not at UEs. That is why we postpone the creation to the moment of setting the device.
237 // The other option would be to pass the device as a parameter to the constructor of the
238 // NrSpectrumPhy. But since NrSpectrumPhy inherits this SetDevice function from SpectrumPhy
239 // class, so passing also device as a parameter to constructor would create a more complicate
240 // interface.
241
242 if (m_isEnb)
243 {
244 m_interferenceSrs = CreateObject<NrInterference>();
245 m_interferenceSrs->TraceConnectWithoutContext(
246 "SnrPerProcessedChunk",
247 MakeCallback(&NrSpectrumPhy::UpdateSrsSnrPerceived, this));
248 }
249 else
250 {
251 m_interferenceData->TraceConnectWithoutContext(
252 "SnrPerProcessedChunk",
253 MakeCallback(&NrSpectrumPhy::ReportWbDlDataSnrPerceived, this));
254 }
255}
256
257Ptr<NetDevice>
258NrSpectrumPhy::GetDevice() const
259{
260 return m_device;
261}
262
263void
264NrSpectrumPhy::SetMobility(Ptr<MobilityModel> m)
265{
266 m_mobility = m;
267}
268
269Ptr<MobilityModel>
270NrSpectrumPhy::GetMobility() const
271{
272 return m_mobility;
273}
274
275void
276NrSpectrumPhy::SetChannel(Ptr<SpectrumChannel> c)
277{
278 m_channel = c;
279}
280
281Ptr<const SpectrumModel>
282NrSpectrumPhy::GetRxSpectrumModel() const
283{
284 return m_rxSpectrumModel;
285}
286
287Ptr<Object>
289{
290 NS_LOG_FUNCTION(this);
291 return m_antenna;
292}
293
294// set/get attributes
295
296void
297NrSpectrumPhy::SetBeamManager(Ptr<BeamManager> b)
298{
299 m_beamManager = b;
300}
301
302Ptr<BeamManager>
303NrSpectrumPhy::GetBeamManager()
304{
305 return m_beamManager;
306}
307
308void
309NrSpectrumPhy::SetErrorModel(Ptr<NrErrorModel> em)
310{
311 NS_LOG_FUNCTION(this << em);
312 m_errorModel = em;
313}
314
315Ptr<NrErrorModel>
316NrSpectrumPhy::GetErrorModel() const
317{
318 return m_errorModel;
319}
320
321void
322NrSpectrumPhy::EnableDlDataPathlossTrace()
323{
324 m_enableDlDataPathlossTrace = true;
325}
326
327void
328NrSpectrumPhy::EnableDlCtrlPathlossTrace()
329{
330 m_enableDlCtrlPathlossTrace = true;
331}
332
333void
334NrSpectrumPhy::SetRnti(uint16_t rnti)
335{
336 m_rnti = rnti;
337 m_hasRnti = true;
338}
339
340void
342{
343 NS_LOG_FUNCTION(this << thresholdDBm);
344 // convert dBm to Watt
345 m_ccaMode1ThresholdW = (std::pow(10.0, thresholdDBm / 10.0)) / 1000.0;
346}
347
348double
350{
351 // convert Watt to dBm
352 return 10.0 * std::log10(m_ccaMode1ThresholdW * 1000.0);
353}
354
355void
357{
358 NS_LOG_FUNCTION(this << unlicensedMode);
359 m_unlicensedMode = unlicensedMode;
360}
361
362void
364{
365 m_dataErrorModelEnabled = dataErrorModelEnabled;
366}
367
368void
370{
371 m_errorModelType = errorModelType;
372}
373
374// other
375
376void
377NrSpectrumPhy::SetNoisePowerSpectralDensity(const Ptr<const SpectrumValue>& noisePsd)
378{
379 NS_LOG_FUNCTION(this << noisePsd);
380 NS_ASSERT(noisePsd);
381 m_rxSpectrumModel = noisePsd->GetSpectrumModel();
382 m_interferenceData->SetNoisePowerSpectralDensity(noisePsd);
383 m_interferenceCtrl->SetNoisePowerSpectralDensity(noisePsd);
384 if (m_interferenceSrs)
385 {
386 m_interferenceSrs->SetNoisePowerSpectralDensity(noisePsd);
387 }
388}
389
390void
391NrSpectrumPhy::SetTxPowerSpectralDensity(const Ptr<SpectrumValue>& TxPsd)
392{
393 m_txPsd = TxPsd;
394}
395
396Ptr<const SpectrumValue>
397NrSpectrumPhy::GetTxPowerSpectralDensity()
398{
399 return m_txPsd;
400}
401
402void
403NrSpectrumPhy::StartRx(Ptr<SpectrumSignalParameters> params)
404{
405 NS_LOG_FUNCTION(this);
406 Ptr<const SpectrumValue> rxPsd = params->psd;
407 Time duration = params->duration;
408 NS_LOG_INFO("Start receiving signal: " << rxPsd << " duration= " << duration);
409
410 // pass it to interference calculations regardless of the type (nr or non-nr)
411 m_interferenceData->AddSignalMimo(params, duration);
412
413 // pass the signal to the interference calculator regardless of the type (nr or non-nr)
414 if (m_interferenceSrs)
415 {
416 m_interferenceSrs->AddSignalMimo(params, duration);
417 }
418
419 Ptr<NrSpectrumSignalParametersDataFrame> nrDataRxParams =
420 DynamicCast<NrSpectrumSignalParametersDataFrame>(params);
421
422 Ptr<NrSpectrumSignalParametersDlCtrlFrame> dlCtrlRxParams =
423 DynamicCast<NrSpectrumSignalParametersDlCtrlFrame>(params);
424
425 Ptr<NrSpectrumSignalParametersUlCtrlFrame> ulCtrlRxParams =
426 DynamicCast<NrSpectrumSignalParametersUlCtrlFrame>(params);
427
428 if (nrDataRxParams)
429 {
430 if (nrDataRxParams->cellId == GetCellId())
431 {
432 // Receive only signals intended for this receiver. Receive only
433 // - if the receiver is a UE and the signal's RNTI matches the UE's RNTI,
434 // - or if the receiver device is either a gNB or not configured (has no RNTI)
435 auto isIntendedRx = (nrDataRxParams->rnti == m_rnti) || !m_hasRnti;
436
437 // Workaround: always receive the signal, to keep performance equal to OSS code.
438 // This ensures all UEs can generate CQI feedback from data signals, even from those
439 // signals that are intended for other UEs in the same cell.
440 // TODO: implement proper CSI-RS signalling for CQI and disable this workaround
441 isIntendedRx = true;
442
443 if (isIntendedRx)
444 {
445 StartRxData(nrDataRxParams);
446 }
447 if (!m_isEnb and m_enableDlDataPathlossTrace)
448 {
449 Ptr<const SpectrumValue> txPsd =
450 DynamicCast<NrSpectrumPhy>(nrDataRxParams->txPhy)->GetTxPowerSpectralDensity();
451 Ptr<const SpectrumValue> rxPsd = nrDataRxParams->psd;
452 double pathloss = 10 * log10(Integral(*txPsd)) - 10 * log10(Integral(*rxPsd));
453 Ptr<NrUePhy> phy = (DynamicCast<NrUePhy>(m_phy));
454 m_dlDataPathlossTrace(GetCellId(),
455 GetBwpId(),
456 GetMobility()->GetObject<Node>()->GetId(),
457 pathloss,
458 phy->ComputeCqi(m_sinrPerceived));
459 }
460 }
461 else
462 {
463 NS_LOG_INFO(" Received DATA not in sync with this signal (cellId="
464 << nrDataRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
465 }
466 }
467 else if (dlCtrlRxParams != nullptr)
468 {
469 m_interferenceCtrl->AddSignalMimo(params, duration);
470
471 if (!m_isEnb)
472 {
473 if (dlCtrlRxParams->pss)
474 {
475 if (dlCtrlRxParams->cellId == GetCellId())
476 {
477 NS_LOG_DEBUG(
478 "Receiving PSS from Serving Cell with Id: " << dlCtrlRxParams->cellId);
479 }
480 else
481 {
482 NS_LOG_DEBUG(
483 "Receiving PSS from Neighbor Cell with Id: " << dlCtrlRxParams->cellId);
484 }
485
486 if (!m_phyRxPssCallback.IsNull())
487 {
488 m_phyRxPssCallback(dlCtrlRxParams->cellId, dlCtrlRxParams->psd);
489 }
490 }
491
492 if (dlCtrlRxParams->cellId == GetCellId())
493 {
494 m_interferenceCtrl->StartRxMimo(params);
495 StartRxDlCtrl(dlCtrlRxParams);
496
497 if (m_enableDlCtrlPathlossTrace)
498 {
499 Ptr<const SpectrumValue> txPsd =
500 DynamicCast<NrSpectrumPhy>(dlCtrlRxParams->txPhy)
501 ->GetTxPowerSpectralDensity();
502 Ptr<const SpectrumValue> rxPsd = dlCtrlRxParams->psd;
503 double pathloss = 10 * log10(Integral(*txPsd)) - 10 * log10(Integral(*rxPsd));
504 m_dlCtrlPathlossTrace(GetCellId(),
505 GetBwpId(),
506 GetMobility()->GetObject<Node>()->GetId(),
507 pathloss);
508 }
509 }
510 else
511 {
512 NS_LOG_INFO("Received DL CTRL, but not in sync with this signal (cellId="
513 << dlCtrlRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
514 }
515 }
516 else
517 {
518 NS_LOG_DEBUG("DL CTRL ignored at gNB");
519 }
520 }
521 else if (ulCtrlRxParams != nullptr)
522 {
523 if (m_isEnb) // only gNBs should enter into reception of UL CTRL signals
524 {
525 if (ulCtrlRxParams->cellId == GetCellId())
526 {
527 if (IsOnlySrs(ulCtrlRxParams->ctrlMsgList))
528 {
529 StartRxSrs(ulCtrlRxParams);
530 }
531 else
532 {
533 StartRxUlCtrl(ulCtrlRxParams);
534 }
535 }
536 else
537 {
538 NS_LOG_INFO("Received UL CTRL, but not in sync with this signal (cellId="
539 << ulCtrlRxParams->cellId << ", m_cellId=" << GetCellId() << ")");
540 }
541 }
542 else
543 {
544 NS_LOG_DEBUG("UL CTRL ignored at UE device");
545 }
546 }
547 else
548 {
549 NS_LOG_INFO("Received non-nr signal of duration:" << duration);
550 }
551
552 // If in RX or TX state, do not change to CCA_BUSY until is finished
553 // RX or TX state. If in IDLE state, then ok, move to CCA_BUSY if the
554 // channel is found busy.
555 if (m_unlicensedMode && m_state == IDLE)
556 {
557 MaybeCcaBusy();
558 }
559}
560
561void
562NrSpectrumPhy::StartTxDataFrames(const Ptr<PacketBurst>& pb,
563 const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
564 const std::shared_ptr<DciInfoElementTdma> dci,
565 const Time& duration)
566{
567 NS_LOG_FUNCTION(this);
568 switch (m_state)
569 {
570 case RX_DATA:
571 /* no break */
572 [[fallthrough]];
573 case RX_DL_CTRL:
574 /* no break */
575 [[fallthrough]];
576 case RX_UL_CTRL:
577 /* no break*/
578 [[fallthrough]];
579 case RX_UL_SRS:
580 NS_FATAL_ERROR("Cannot TX while RX.");
581 break;
582 case TX:
583 // No break, gNB may transmit multiple times to multiple UEs
584 [[fallthrough]];
585 case CCA_BUSY:
586 NS_LOG_WARN("Start transmitting DATA while in CCA_BUSY state.");
587 /* no break */
588 [[fallthrough]];
589 case IDLE: {
590 NS_ASSERT(m_txPsd);
591
592 ChangeState(TX, duration);
593
594 Ptr<NrSpectrumSignalParametersDataFrame> txParams =
595 Create<NrSpectrumSignalParametersDataFrame>();
596 txParams->duration = duration;
597 txParams->txPhy = this->GetObject<SpectrumPhy>();
598 txParams->psd = m_txPsd;
599 txParams->packetBurst = pb;
600 txParams->cellId = GetCellId();
601 txParams->ctrlMsgList = ctrlMsgList;
602 txParams->rnti = dci->m_rnti;
603 txParams->precodingMatrix = dci->m_precMats;
604
605 /* This section is used for trace */
606 if (m_isEnb)
607 {
609 traceParam.m_noBytes = (txParams->packetBurst) ? txParams->packetBurst->GetSize() : 0;
610 traceParam.m_cellId = txParams->cellId;
611 traceParam.m_isTx = true;
612 traceParam.m_subframeno = 0; // TODO extend this
613
614 m_txPacketTraceEnb(traceParam);
615 }
616
617 m_txDataTrace(duration);
618
619 if (m_channel)
620 {
621 m_channel->StartTx(txParams);
622 }
623 else
624 {
625 NS_LOG_WARN("Working without channel (i.e., under test)");
626 }
627
628 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
629 m_activeTransmissions++;
630 }
631 break;
632 default:
633 NS_LOG_FUNCTION(this << "Programming Error. Code should not reach this point");
634 }
635}
636
637bool
639{
640 return m_state == TX;
641}
642
643void
644NrSpectrumPhy::StartTxDlControlFrames(const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
645 const Time& duration)
646{
647 NS_LOG_LOGIC(this << " state: " << m_state);
648
649 switch (m_state)
650 {
651 case RX_DATA:
652 /* no break */
653 case RX_DL_CTRL:
654 /* no break */
655 case RX_UL_CTRL:
656 /* no break*/
657 case RX_UL_SRS:
658 NS_FATAL_ERROR("Cannot TX while RX.");
659 break;
660 case TX:
661 NS_FATAL_ERROR("Cannot TX while already TX.");
662 break;
663 case CCA_BUSY:
664 NS_LOG_WARN("Start transmitting DL CTRL while in CCA_BUSY state.");
665 /* no break */
666 case IDLE: {
667 NS_ASSERT(m_txPsd);
668 ChangeState(TX, duration);
669 Ptr<NrSpectrumSignalParametersDlCtrlFrame> txParams =
670 Create<NrSpectrumSignalParametersDlCtrlFrame>();
671 txParams->duration = duration;
672 txParams->txPhy = GetObject<SpectrumPhy>();
673 txParams->psd = m_txPsd;
674 txParams->cellId = GetCellId();
675 txParams->pss = true;
676 txParams->ctrlMsgList = ctrlMsgList;
677
678 m_txCtrlTrace(duration);
679 if (m_channel)
680 {
681 m_channel->StartTx(txParams);
682 }
683 else
684 {
685 NS_LOG_WARN("Working without channel (i.e., under test)");
686 }
687
688 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
689 m_activeTransmissions++;
690 }
691 }
692}
693
694void
695NrSpectrumPhy::StartTxUlControlFrames(const std::list<Ptr<NrControlMessage>>& ctrlMsgList,
696 const Time& duration)
697{
698 NS_LOG_LOGIC(this << " state: " << m_state);
699
700 switch (m_state)
701 {
702 case RX_DATA:
703 /* no break */
704 case RX_DL_CTRL:
705 /* no break */
706 case RX_UL_CTRL:
707 /* no break */
708 case RX_UL_SRS:
709 NS_FATAL_ERROR("Cannot TX while RX.");
710 break;
711 case TX:
712 NS_FATAL_ERROR("Cannot TX while already TX.");
713 break;
714 case CCA_BUSY:
715 NS_LOG_WARN("Start transmitting UL CTRL while in CCA_BUSY state");
716 /* no break */
717 case IDLE: {
718 NS_ASSERT(m_txPsd);
719 ChangeState(TX, duration);
720 Ptr<NrSpectrumSignalParametersUlCtrlFrame> txParams =
721 Create<NrSpectrumSignalParametersUlCtrlFrame>();
722 txParams->duration = duration;
723 txParams->txPhy = GetObject<SpectrumPhy>();
724 txParams->psd = m_txPsd;
725 txParams->cellId = GetCellId();
726 txParams->ctrlMsgList = ctrlMsgList;
727
728 m_txCtrlTrace(duration);
729 if (m_channel)
730 {
731 m_channel->StartTx(txParams);
732 }
733 else
734 {
735 NS_LOG_WARN("Working without channel (i.e., under test)");
736 }
737 Simulator::Schedule(duration, &NrSpectrumPhy::EndTx, this);
738 m_activeTransmissions++;
739 }
740 }
741}
742
743void
744NrSpectrumPhy::AddDataPowerChunkProcessor(const Ptr<LteChunkProcessor>& p)
745{
746 NS_LOG_FUNCTION(this);
747 m_interferenceData->AddRsPowerChunkProcessor(p);
748}
749
750void
751NrSpectrumPhy::AddDataSinrChunkProcessor(const Ptr<LteChunkProcessor>& p)
752{
753 NS_LOG_FUNCTION(this);
754 m_interferenceData->AddSinrChunkProcessor(p);
755}
756
757void
758NrSpectrumPhy::AddSrsSinrChunkProcessor(const Ptr<LteChunkProcessor>& p)
759{
760 NS_LOG_FUNCTION(this);
761 NS_ASSERT_MSG(m_isEnb && m_interferenceSrs,
762 "SRS interference object does not exist or this device is not gNb so the "
763 "function should not be called.");
764 m_interferenceSrs->AddSinrChunkProcessor(p);
765}
766
767void
768NrSpectrumPhy::ReportDlCtrlSinr(const SpectrumValue& sinr)
769{
770 NS_LOG_FUNCTION(this);
771 Ptr<NrUePhy> phy = (DynamicCast<NrUePhy>(m_phy));
772 NS_ABORT_MSG_UNLESS(
773 phy,
774 "This function should only be called for NrSpectrumPhy belonging to NrUEPhy");
775 phy->ReportDlCtrlSinr(sinr);
776}
777
778void
779NrSpectrumPhy::UpdateSrsSinrPerceived(const SpectrumValue& srsSinr)
780{
781 NS_LOG_FUNCTION(this << srsSinr);
782 NS_LOG_INFO("Update SRS SINR perceived with this value: " << srsSinr);
783
784 for (auto& srsCallback : m_srsSinrReportCallback)
785 {
786 srsCallback(GetCellId(),
787 m_currentSrsRnti,
788 Sum(srsSinr) / (srsSinr.GetSpectrumModel()->GetNumBands()));
789 }
790}
791
792void
794{
795 NS_LOG_FUNCTION(this << srsSnr);
796 NS_LOG_INFO("Update SRS SNR perceived with this value: " << srsSnr);
797
798 for (auto& srsSnrCallback : m_srsSnrReportCallback)
799 {
800 srsSnrCallback(GetCellId(), m_currentSrsRnti, srsSnr);
801 }
802}
803
804void
805NrSpectrumPhy::AddRsPowerChunkProcessor(const Ptr<LteChunkProcessor>& p)
806{
807 NS_LOG_FUNCTION(this);
808 m_interferenceCtrl->AddRsPowerChunkProcessor(p);
809}
810
811void
812NrSpectrumPhy::AddDlCtrlSinrChunkProcessor(const Ptr<LteChunkProcessor>& p)
813{
814 NS_LOG_FUNCTION(this);
815 m_interferenceCtrl->AddSinrChunkProcessor(p);
816}
817
818void
819NrSpectrumPhy::UpdateSinrPerceived(const SpectrumValue& sinr)
820{
821 NS_LOG_FUNCTION(this << sinr);
822 NS_LOG_INFO("Update SINR perceived with this value: " << sinr);
823 m_sinrPerceived = sinr;
824}
825
826void
827NrSpectrumPhy::InstallHarqPhyModule(const Ptr<NrHarqPhy>& harq)
828{
829 NS_ABORT_IF(m_harqPhyModule != nullptr);
830 m_harqPhyModule = harq;
831}
832
833void
834NrSpectrumPhy::InstallPhy(const Ptr<NrPhy>& phyModel)
835{
836 m_phy = phyModel;
837}
838
839Ptr<NrPhy>
840NrSpectrumPhy::GetPhy() const
841{
842 return m_phy;
843}
844
845void
846NrSpectrumPhy::SetAntenna(const Ptr<Object> antenna)
847{
848 m_antenna = antenna;
849}
850
851Ptr<SpectrumChannel>
853{
854 return m_channel;
855}
856
857Ptr<NrHarqPhy>
859{
860 return m_harqPhyModule;
861}
862
863Ptr<NrInterference>
865{
866 NS_LOG_FUNCTION(this);
867 return m_interferenceData;
868}
869
870void
872 uint8_t ndi,
873 uint32_t size,
874 uint8_t mcs,
875 uint8_t rank,
876 const std::vector<int>& rbMap,
877 uint8_t harqId,
878 uint8_t rv,
879 bool downlink,
880 uint8_t symStart,
881 uint8_t numSym,
882 const SfnSf& sfn)
883{
884 NS_LOG_FUNCTION(this);
885
886 if (!IsEnb())
887 {
888 NS_ASSERT_MSG(m_hasRnti, "Cannot send TB to a UE whose RNTI has not been set");
889 NS_ASSERT_MSG(m_rnti == rnti, "RNTI of the receiving UE must match the RNTI of the TB");
890 }
891
892 auto it = m_transportBlocks.find(rnti);
893 if (it != m_transportBlocks.end())
894 {
895 // might be a TB of an unreceived packet (due to high propagation losses)
896 m_transportBlocks.erase(it);
897 }
898
899 m_transportBlocks.emplace(rnti,
900 TransportBlockInfo(ExpectedTb(ndi,
901 size,
902 mcs,
903 rank,
904 rnti,
905 rbMap,
906 harqId,
907 rv,
908 downlink,
909 symStart,
910 numSym,
911 sfn)));
912 NS_LOG_INFO("Add expected TB for rnti "
913 << rnti << " size=" << size << " mcs=" << static_cast<uint32_t>(mcs) << " symstart="
914 << static_cast<uint32_t>(symStart) << " numSym=" << static_cast<uint32_t>(numSym));
915}
916
917void
918NrSpectrumPhy::AddExpectedSrsRnti(uint16_t rnti)
919{
920 m_currentSrsRnti = rnti;
921}
922
923void
924NrSpectrumPhy::AddSrsSinrReportCallback(SrsSinrReportCallback callback)
925{
926 m_srsSinrReportCallback.push_back(callback);
927}
928
929void
930NrSpectrumPhy::AddSrsSnrReportCallback(SrsSnrReportCallback callback)
931{
932 m_srsSnrReportCallback.push_back(callback);
933}
934
935// private
936
937void
938NrSpectrumPhy::StartRxData(const Ptr<NrSpectrumSignalParametersDataFrame>& params)
939{
940 NS_LOG_FUNCTION(this);
941
942 m_rxDataTrace(m_phy->GetCurrentSfnSf(),
943 params->psd,
944 params->duration,
945 m_phy->GetBwpId(),
946 m_phy->GetCellId());
947
948 switch (m_state)
949 {
950 case TX:
951 if (m_isEnb) // I am gNB. We are here because some of my rebellious UEs is transmitting
952 // at the same time as me. -> invalid state.
953 {
954 NS_FATAL_ERROR("eNB transmission overlaps in time with UE transmission. CellId:"
955 << params->cellId);
956 }
957 else // I am UE, and while I am transmitting, someone else also transmits. If we are
958 // transmitting on orthogonal TX PSDs then this is most probably valid situation
959 // (UEs transmitting to gNB).
960 {
961 // Sanity check, that we do not transmit on the same RBs; this sanity check will not
962 // be the same for sidelink/V2X
963 NS_ASSERT_MSG((Sum((*m_txPsd) * (*params->psd)) == 0),
964 "Transmissions overlap in frequency. Their cellId is:" << params->cellId);
965 return;
966 }
967 break;
968 case RX_DL_CTRL:
969 /* no break */
970 case RX_UL_CTRL:
971 /* no break */
972 case RX_UL_SRS:
973 NS_FATAL_ERROR("Cannot receive DATA while receiving CTRL.");
974 break;
975 case CCA_BUSY:
976 NS_LOG_INFO("Start receiving DATA while in CCA_BUSY state.");
977 /* no break */
978 case RX_DATA: // RX_DATA while RX_DATA is possible with OFDMA, i.e. gNB receives from
979 // multiple UEs at the same time
980 /* no break */
981 case IDLE: {
982 m_interferenceData->StartRxMimo(params);
983
984 if (m_rxPacketBurstList.empty())
985 {
986 NS_ASSERT(m_state == IDLE || m_state == CCA_BUSY);
987 // first transmission, i.e., we're IDLE and we start RX
988 m_firstRxStart = Simulator::Now();
989 m_firstRxDuration = params->duration;
990 NS_LOG_LOGIC(this << " scheduling EndRx with delay " << params->duration.GetSeconds()
991 << "s");
992
993 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxData, this);
994 }
995 else
996 {
997 NS_ASSERT(m_state == RX_DATA);
998 // sanity check: if there are multiple RX events, they
999 // should occur at the same time and have the same
1000 // duration, otherwise the interference calculation
1001 // won't be correct
1002 NS_ASSERT((m_firstRxStart == Simulator::Now()) &&
1003 (m_firstRxDuration == params->duration));
1004 }
1005
1006 ChangeState(RX_DATA, params->duration);
1007
1008 if (params->packetBurst && !params->packetBurst->GetPackets().empty())
1009 {
1010 m_rxPacketBurstList.push_back(params->packetBurst);
1011 }
1012 // NS_LOG_DEBUG (this << " insert msgs " << params->ctrlMsgList.size ());
1013 m_rxControlMessageList.insert(m_rxControlMessageList.end(),
1014 params->ctrlMsgList.begin(),
1015 params->ctrlMsgList.end());
1016
1017 NS_LOG_LOGIC(this << " numSimultaneousRxEvents = " << m_rxPacketBurstList.size());
1018 }
1019 break;
1020 default:
1021 NS_FATAL_ERROR("Programming Error: Unknown State");
1022 }
1023}
1024
1025void
1026NrSpectrumPhy::StartRxDlCtrl(const Ptr<NrSpectrumSignalParametersDlCtrlFrame>& params)
1027{
1028 // The current code of this function assumes:
1029 // that this function is called only when cellId = m_cellId, which means
1030 // that UE can start to receive DL CTRL only from its own cellId,
1031 // and CTRL from other cellIds will be ignored
1032 NS_LOG_FUNCTION(this);
1033 NS_ASSERT(params->cellId == GetCellId() && !m_isEnb);
1034 // RDF: method currently supports Downlink control only!
1035 switch (m_state)
1036 {
1037 case TX:
1038 NS_FATAL_ERROR("Cannot RX while TX.");
1039 break;
1040 case RX_DATA:
1041 NS_FATAL_ERROR("Cannot RX CTRL while receiving DATA.");
1042 break;
1043 case RX_DL_CTRL:
1044 NS_FATAL_ERROR("Cannot RX DL CTRL while already receiving DL CTRL.");
1045 break;
1046 case RX_UL_CTRL:
1047 /* no break */
1048 case RX_UL_SRS:
1049 NS_FATAL_ERROR("UE should never be in RX_UL_CTRL or RX_UL_SRS state.");
1050 break;
1051 case CCA_BUSY:
1052 NS_LOG_INFO("Start receiving CTRL while channel in CCA_BUSY state.");
1053 /* no break */
1054 case IDLE: {
1055 NS_ASSERT(m_rxControlMessageList.empty());
1056 NS_LOG_LOGIC(this << "receiving DL CTRL from cellId:" << params->cellId
1057 << "and scheduling EndRx with delay " << params->duration);
1058 // store the DCIs
1059 m_rxControlMessageList = params->ctrlMsgList;
1060 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxCtrl, this);
1061 ChangeState(RX_DL_CTRL, params->duration);
1062 break;
1063 }
1064 default: {
1065 NS_FATAL_ERROR("Unknown state.");
1066 break;
1067 }
1068 }
1069}
1070
1071void
1072NrSpectrumPhy::StartRxUlCtrl(const Ptr<NrSpectrumSignalParametersUlCtrlFrame>& params)
1073{
1074 // The current code of this function assumes:
1075 // 1) that this function is called only when cellId = m_cellId
1076 // 2) this function should be only called for gNB, only gNB should enter into reception of
1077 // UL CTRL signals 3) gNB can receive simultaneously signals from various UEs
1078 NS_LOG_FUNCTION(this);
1079 NS_ASSERT(params->cellId == GetCellId() && m_isEnb);
1080 // RDF: method currently supports Uplink control only!
1081 switch (m_state)
1082 {
1083 case TX:
1084 NS_FATAL_ERROR("Cannot RX UL CTRL while TX.");
1085 break;
1086 case RX_DATA:
1087 NS_FATAL_ERROR("Cannot RX UL CTRL while receiving DATA.");
1088 break;
1089 case RX_UL_SRS:
1090 NS_FATAL_ERROR("Cannot start RX UL CTRL while already receiving SRS.");
1091 break;
1092 case RX_DL_CTRL:
1093 NS_FATAL_ERROR("gNB should not be in RX_DL_CTRL state.");
1094 break;
1095 case CCA_BUSY:
1096 NS_LOG_INFO("Start receiving UL CTRL while channel in CCA_BUSY state.");
1097 /* no break */
1098 case RX_UL_CTRL:
1099 /* no break */
1100 case IDLE: {
1101 // at the gNB we can receive more UL CTRL signals simultaneously
1102 if (m_state == IDLE || m_state == CCA_BUSY)
1103 {
1104 // first transmission, i.e., we're IDLE and we start RX
1105 NS_ASSERT(m_rxControlMessageList.empty());
1106 m_firstRxStart = Simulator::Now();
1107 m_firstRxDuration = params->duration;
1108 NS_LOG_LOGIC(this << " scheduling EndRx with delay " << params->duration);
1109 // store the DCIs
1110 m_rxControlMessageList = params->ctrlMsgList;
1111 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxCtrl, this);
1112 ChangeState(RX_UL_CTRL, params->duration);
1113 }
1114 else // already in RX_UL_CTRL state, just add new CTRL messages from other UE
1115 {
1116 NS_ASSERT((m_firstRxStart == Simulator::Now()) &&
1117 (m_firstRxDuration == params->duration));
1118 m_rxControlMessageList.insert(m_rxControlMessageList.end(),
1119 params->ctrlMsgList.begin(),
1120 params->ctrlMsgList.end());
1121 }
1122 break;
1123 }
1124 default: {
1125 NS_FATAL_ERROR("unknown state");
1126 break;
1127 }
1128 }
1129}
1130
1131void
1132NrSpectrumPhy::StartRxSrs(const Ptr<NrSpectrumSignalParametersUlCtrlFrame>& params)
1133{
1134 NS_LOG_FUNCTION(this);
1135 // The current code of this function assumes:
1136 // 1) that this function is called only when cellId = m_cellId
1137 // 2) this function should be only called for gNB, only gNB should enter into reception of
1138 // UL SRS signals 3) SRS should be received only one at a time, otherwise this function
1139 // should assert 4) CTRL message list contains only one message and that one is SRS CTRL
1140 // message
1141 NS_ASSERT(params->cellId == GetCellId() && m_isEnb && m_state != RX_UL_SRS &&
1142 params->ctrlMsgList.size() == 1 &&
1143 (*params->ctrlMsgList.begin())->GetMessageType() == NrControlMessage::SRS);
1144
1145 switch (m_state)
1146 {
1147 case TX:
1148 NS_FATAL_ERROR("Cannot RX SRS while TX.");
1149 break;
1150 case RX_DATA:
1151 NS_FATAL_ERROR("Cannot RX SRS while receiving DATA.");
1152 break;
1153 case RX_DL_CTRL:
1154 NS_FATAL_ERROR("gNB should not be in RX_DL_CTRL state.");
1155 break;
1156 case RX_UL_CTRL:
1157 NS_FATAL_ERROR(
1158 "gNB should not receive simultaneously non SRS and SRS uplink control signals");
1159 break;
1160 case CCA_BUSY:
1161 NS_LOG_INFO("Start receiving UL SRS while channel in CCA_BUSY state.");
1162 /* no break */
1163 case IDLE: {
1164 // at the gNB we can receive only one SRS at a time, and the only allowed states before
1165 // starting it are IDLE or BUSY
1166 m_interferenceSrs->StartRxMimo(params);
1167 // first transmission, i.e., we're IDLE and we start RX, CTRL message list should be empty
1168 NS_ASSERT(m_rxControlMessageList.empty());
1169 m_firstRxStart = Simulator::Now();
1170 m_firstRxDuration = params->duration;
1171 NS_LOG_LOGIC(this << " scheduling EndRx for SRS signal reception with delay "
1172 << params->duration);
1173 // store the SRS message in the CTRL message list
1174 m_rxControlMessageList = params->ctrlMsgList;
1175 Simulator::Schedule(params->duration, &NrSpectrumPhy::EndRxSrs, this);
1176 ChangeState(RX_UL_SRS, params->duration);
1177 }
1178 break;
1179 default: {
1180 // not allowed state for starting the SRS reception
1181 NS_FATAL_ERROR("Not allowed state for starting SRS reception.");
1182 break;
1183 }
1184 }
1185}
1186
1187uint16_t
1189{
1190 return m_phy->GetCellId();
1191}
1192
1193uint16_t
1195{
1196 return m_phy->GetBwpId();
1197}
1198
1199bool
1200NrSpectrumPhy::IsEnb() const
1201{
1202 return m_isEnb;
1203}
1204
1205void
1207{
1208 m_isEnb = isEnb;
1209}
1210
1211void
1212NrSpectrumPhy::ChangeState(State newState, Time duration)
1213{
1214 NS_LOG_LOGIC(this << " change state: " << m_state << " -> " << newState);
1215 m_state = newState;
1216
1217 if (newState == RX_DATA || newState == RX_DL_CTRL || newState == RX_UL_CTRL || newState == TX ||
1218 newState == CCA_BUSY)
1219 {
1220 m_channelOccupied(duration);
1221 }
1222}
1223
1224void
1225NrSpectrumPhy::EndTx()
1226{
1227 NS_LOG_FUNCTION(this);
1228
1229 // In case of OFDMA DL, this function will be called multiple times, after each transmission to
1230 // a different UE. In the first call to this function, m_state is changed to IDLE.
1231 NS_ASSERT_MSG(m_state == TX, "In EndTx() but state is not TX; state: " << m_state);
1232 NS_LOG_DEBUG("Number of active transmissions (before decrement): " << m_activeTransmissions);
1233 NS_ASSERT_MSG(m_activeTransmissions, "Ending Tx but no active transmissions");
1234 m_activeTransmissions--;
1235
1236 // change to BUSY or IDLE mode when this is the end of the last transmission
1237 if (m_activeTransmissions == 0)
1238 {
1239 // if in unlicensed mode check after transmission if we are in IDLE or CCA_BUSY mode
1240 if (m_unlicensedMode)
1241 {
1242 MaybeCcaBusy();
1243 }
1244 else
1245 {
1246 ChangeState(IDLE, Seconds(0));
1247 }
1248 }
1249}
1250
1251std::vector<MimoSinrChunk>
1252NrSpectrumPhy::GetMimoSinrForRnti(uint16_t rnti, uint8_t rank)
1253{
1254 // Filter chunks by RNTI of the expected TB. For DL, this step selects only the RX signals that
1255 // were sent towards this UE. For UL, it selects only signals that were sent from the UE that is
1256 // currently being decoded.
1257 std::vector<MimoSinrChunk> res;
1258 for (const auto& chunk : m_mimoSinrPerceived)
1259 {
1260 if (chunk.rnti == rnti)
1261 {
1262 res.emplace_back(chunk);
1263 }
1264 }
1265 if (res.empty())
1266 {
1267 // No received signal found, create all-zero SINR matrix with minimum duration
1268 NS_LOG_WARN("Did not find any SINR matrix matching the current UE's RNTI " << rnti);
1269 auto sinrMat = NrSinrMatrix{rank, m_rxSpectrumModel->GetNumBands()};
1270 auto dur = NanoSeconds(1);
1271 res.emplace_back(MimoSinrChunk{sinrMat, rnti, dur});
1272 }
1273 return res;
1274}
1275
1276void
1277NrSpectrumPhy::EndRxData()
1278{
1279 NS_LOG_FUNCTION(this);
1280 m_interferenceData->EndRx();
1281
1282 Ptr<NrGnbNetDevice> enbRx = DynamicCast<NrGnbNetDevice>(GetDevice());
1283 Ptr<NrUeNetDevice> ueRx = DynamicCast<NrUeNetDevice>(GetDevice());
1284
1285 NS_ASSERT(m_state == RX_DATA);
1286
1287 GetSecond GetTBInfo;
1288 GetFirst GetRnti;
1289
1290 for (auto& tbIt : m_transportBlocks)
1291 {
1292 GetTBInfo(tbIt).m_sinrAvg = 0.0;
1293 GetTBInfo(tbIt).m_sinrMin = 99999999999;
1294 for (const auto& rbIndex : GetTBInfo(tbIt).m_expected.m_rbBitmap)
1295 {
1296 GetTBInfo(tbIt).m_sinrAvg += m_sinrPerceived.ValuesAt(rbIndex);
1297 if (m_sinrPerceived.ValuesAt(rbIndex) < GetTBInfo(tbIt).m_sinrMin)
1298 {
1299 GetTBInfo(tbIt).m_sinrMin = m_sinrPerceived.ValuesAt(rbIndex);
1300 }
1301 }
1302
1303 GetTBInfo(tbIt).m_sinrAvg =
1304 GetTBInfo(tbIt).m_sinrAvg / GetTBInfo(tbIt).m_expected.m_rbBitmap.size();
1305
1306 NS_LOG_INFO("Finishing RX, sinrAvg=" << GetTBInfo(tbIt).m_sinrAvg << " sinrMin="
1307 << GetTBInfo(tbIt).m_sinrMin << " SinrAvg (dB) "
1308 << 10 * log(GetTBInfo(tbIt).m_sinrAvg) / log(10));
1309
1310 if ((!m_dataErrorModelEnabled) || (m_rxPacketBurstList.empty()))
1311 {
1312 continue;
1313 }
1314
1315 std::function<const NrErrorModel::NrErrorModelHistory&(uint16_t, uint8_t)> RetrieveHistory;
1316
1317 if (GetTBInfo(tbIt).m_expected.m_isDownlink)
1318 {
1319 RetrieveHistory = std::bind(&NrHarqPhy::GetHarqProcessInfoDl,
1320 m_harqPhyModule,
1321 std::placeholders::_1,
1322 std::placeholders::_2);
1323 }
1324 else
1325 {
1326 RetrieveHistory = std::bind(&NrHarqPhy::GetHarqProcessInfoUl,
1327 m_harqPhyModule,
1328 std::placeholders::_1,
1329 std::placeholders::_2);
1330 }
1331
1332 const NrErrorModel::NrErrorModelHistory& harqInfoList =
1333 RetrieveHistory(GetRnti(tbIt), GetTBInfo(tbIt).m_expected.m_harqProcessId);
1334
1335 NS_ABORT_MSG_IF(!m_errorModelType.IsChildOf(NrErrorModel::GetTypeId()),
1336 "The error model must be a child of NrErrorModel");
1337
1338 if (!m_errorModel)
1339 {
1340 ObjectFactory emFactory;
1341 emFactory.SetTypeId(m_errorModelType);
1342 m_errorModel = DynamicCast<NrErrorModel>(emFactory.Create());
1343 NS_ABORT_IF(m_errorModel == nullptr);
1344 }
1345
1346 // Output is the output of the error model. From the TBLER we decide
1347 // if the entire TB is corrupted or not
1348
1349 if (!m_mimoSinrPerceived.empty())
1350 {
1351 // The received signal information supports MIMO
1352 const auto& expectedTb = GetTBInfo(tbIt).m_expected;
1353 auto sinrChunks = GetMimoSinrForRnti(expectedTb.m_rnti, expectedTb.m_rank);
1354 NS_ASSERT(!sinrChunks.empty());
1355
1356 GetTBInfo(tbIt).m_outputOfEM =
1357 m_errorModel->GetTbDecodificationStatsMimo(sinrChunks,
1358 expectedTb.m_rbBitmap,
1359 expectedTb.m_tbSize,
1360 expectedTb.m_mcs,
1361 expectedTb.m_rank,
1362 harqInfoList);
1363 }
1364 else
1365 {
1366 // SISO code, required only when there is no NrMimoChunkProcessor
1367 // TODO: change nr-uplink-power-control-test to create a 3gpp channel, and remove this
1368 // code
1369 GetTBInfo(tbIt).m_outputOfEM =
1370 m_errorModel->GetTbDecodificationStats(m_sinrPerceived,
1371 GetTBInfo(tbIt).m_expected.m_rbBitmap,
1372 GetTBInfo(tbIt).m_expected.m_tbSize,
1373 GetTBInfo(tbIt).m_expected.m_mcs,
1374 harqInfoList);
1375 }
1376
1377 GetTBInfo(tbIt).m_isCorrupted =
1378 m_random->GetValue() <= GetTBInfo(tbIt).m_outputOfEM->m_tbler;
1379
1380 if (GetTBInfo(tbIt).m_isCorrupted)
1381 {
1382 NS_LOG_INFO("RNTI " << GetRnti(tbIt) << " processId "
1383 << +GetTBInfo(tbIt).m_expected.m_harqProcessId << " size "
1384 << GetTBInfo(tbIt).m_expected.m_tbSize << " mcs "
1385 << (uint32_t)GetTBInfo(tbIt).m_expected.m_mcs << "rank"
1386 << +GetTBInfo(tbIt).m_expected.m_rank << " bitmap "
1387 << GetTBInfo(tbIt).m_expected.m_rbBitmap.size()
1388 << " rv from MAC: " << +GetTBInfo(tbIt).m_expected.m_rv
1389 << " elements in the history: " << harqInfoList.size() << " TBLER "
1390 << GetTBInfo(tbIt).m_outputOfEM->m_tbler << " corrupted "
1391 << GetTBInfo(tbIt).m_isCorrupted);
1392 }
1393 }
1394
1395 std::map<uint16_t, DlHarqInfo> harqDlInfoMap;
1396 for (auto packetBurst : m_rxPacketBurstList)
1397 {
1398 for (auto packet : packetBurst->GetPackets())
1399 {
1400 if (packet->GetSize() == 0)
1401 {
1402 continue;
1403 }
1404
1405 LteRadioBearerTag bearerTag;
1406 if (!packet->PeekPacketTag(bearerTag))
1407 {
1408 NS_FATAL_ERROR("No radio bearer tag found");
1409 }
1410
1411 uint16_t rnti = bearerTag.GetRnti();
1412
1413 auto itTb = m_transportBlocks.find(rnti);
1414
1415 if (itTb == m_transportBlocks.end())
1416 {
1417 // Packet for other device...
1418 continue;
1419 }
1420 if (!GetTBInfo(*itTb).m_isCorrupted)
1421 {
1422 m_phyRxDataEndOkCallback(packet);
1423 }
1424 else
1425 {
1426 NS_LOG_INFO("TB failed");
1427 }
1428
1429 RxPacketTraceParams traceParams;
1430 traceParams.m_tbSize = GetTBInfo(*itTb).m_expected.m_tbSize;
1431 traceParams.m_frameNum = GetTBInfo(*itTb).m_expected.m_sfn.GetFrame();
1432 traceParams.m_subframeNum = GetTBInfo(*itTb).m_expected.m_sfn.GetSubframe();
1433 traceParams.m_slotNum = GetTBInfo(*itTb).m_expected.m_sfn.GetSlot();
1434 traceParams.m_rnti = rnti;
1435 traceParams.m_mcs = GetTBInfo(*itTb).m_expected.m_mcs;
1436 traceParams.m_rank = GetTBInfo(*itTb).m_expected.m_rank;
1437 traceParams.m_rv = GetTBInfo(*itTb).m_expected.m_rv;
1438 traceParams.m_sinr = GetTBInfo(*itTb).m_sinrAvg;
1439 traceParams.m_sinrMin = GetTBInfo(*itTb).m_sinrMin;
1440 if (m_dataErrorModelEnabled)
1441 {
1442 traceParams.m_tbler = GetTBInfo(*itTb).m_outputOfEM->m_tbler;
1443 traceParams.m_corrupt = GetTBInfo(*itTb).m_isCorrupted;
1444 }
1445 else
1446 {
1447 // when error model is disabled a received TB has no
1448 // error, thus, TBLER would be 0 and it would be
1449 // considered as not corrupt.
1450 traceParams.m_tbler = 0;
1451 traceParams.m_corrupt = false;
1452 }
1453 traceParams.m_symStart = GetTBInfo(*itTb).m_expected.m_symStart;
1454 traceParams.m_numSym = GetTBInfo(*itTb).m_expected.m_numSym;
1455 traceParams.m_bwpId = GetBwpId();
1456 traceParams.m_rbAssignedNum =
1457 static_cast<uint32_t>(GetTBInfo(*itTb).m_expected.m_rbBitmap.size());
1458
1459 if (enbRx)
1460 {
1461 traceParams.m_cellId = enbRx->GetCellId();
1462 m_rxPacketTraceEnb(traceParams);
1463 }
1464 else if (ueRx)
1465 {
1466 traceParams.m_cellId = ueRx->GetTargetEnb()->GetCellId();
1467 Ptr<NrUePhy> phy = (DynamicCast<NrUePhy>(m_phy));
1468 traceParams.m_cqi = phy->ComputeCqi(m_sinrPerceived);
1469 m_rxPacketTraceUe(traceParams);
1470 }
1471
1472 // send HARQ feedback (if not already done for this TB)
1473 if (!GetTBInfo(*itTb).m_harqFeedbackSent)
1474 {
1475 GetTBInfo(*itTb).m_harqFeedbackSent = true;
1476 if (!GetTBInfo(*itTb).m_expected.m_isDownlink) // UPLINK TB
1477 {
1478 // Generate the feedback
1479 UlHarqInfo harqUlInfo;
1480 harqUlInfo.m_rnti = rnti;
1481 harqUlInfo.m_tpc = 0;
1482 harqUlInfo.m_harqProcessId = GetTBInfo(*itTb).m_expected.m_harqProcessId;
1483 harqUlInfo.m_numRetx = GetTBInfo(*itTb).m_expected.m_rv;
1484 if (GetTBInfo(*itTb).m_isCorrupted)
1485 {
1486 harqUlInfo.m_receptionStatus = UlHarqInfo::NotOk;
1487 }
1488 else
1489 {
1490 harqUlInfo.m_receptionStatus = UlHarqInfo::Ok;
1491 }
1492
1493 // Send the feedback
1494 if (!m_phyUlHarqFeedbackCallback.IsNull())
1495 {
1496 m_phyUlHarqFeedbackCallback(harqUlInfo);
1497 }
1498
1499 // Arrange the history
1500 if (!GetTBInfo(*itTb).m_isCorrupted || GetTBInfo(*itTb).m_expected.m_rv == 3)
1501 {
1502 m_harqPhyModule->ResetUlHarqProcessStatus(
1503 rnti,
1504 GetTBInfo(*itTb).m_expected.m_harqProcessId);
1505 }
1506 else
1507 {
1508 m_harqPhyModule->UpdateUlHarqProcessStatus(
1509 rnti,
1510 GetTBInfo(*itTb).m_expected.m_harqProcessId,
1511 GetTBInfo(*itTb).m_outputOfEM);
1512 }
1513 }
1514 else
1515 {
1516 // Generate the feedback
1517 DlHarqInfo harqDlInfo;
1518 harqDlInfo.m_rnti = rnti;
1519 harqDlInfo.m_harqProcessId = GetTBInfo(*itTb).m_expected.m_harqProcessId;
1520 harqDlInfo.m_numRetx = GetTBInfo(*itTb).m_expected.m_rv;
1521 harqDlInfo.m_bwpIndex = GetBwpId();
1522 if (GetTBInfo(*itTb).m_isCorrupted)
1523 {
1524 harqDlInfo.m_harqStatus = DlHarqInfo::NACK;
1525 }
1526 else
1527 {
1528 harqDlInfo.m_harqStatus = DlHarqInfo::ACK;
1529 }
1530
1531 NS_ASSERT(harqDlInfoMap.find(rnti) == harqDlInfoMap.end());
1532 harqDlInfoMap.insert(std::make_pair(rnti, harqDlInfo));
1533
1534 // Send the feedback
1535 if (!m_phyDlHarqFeedbackCallback.IsNull())
1536 {
1537 m_phyDlHarqFeedbackCallback(harqDlInfo);
1538 }
1539
1540 // Arrange the history
1541 if (!GetTBInfo(*itTb).m_isCorrupted || GetTBInfo(*itTb).m_expected.m_rv == 3)
1542 {
1543 NS_LOG_DEBUG("Reset Dl process: "
1544 << +GetTBInfo(*itTb).m_expected.m_harqProcessId << " for RNTI "
1545 << rnti);
1546 m_harqPhyModule->ResetDlHarqProcessStatus(
1547 rnti,
1548 GetTBInfo(*itTb).m_expected.m_harqProcessId);
1549 }
1550 else
1551 {
1552 NS_LOG_DEBUG("Update Dl process: "
1553 << +GetTBInfo(*itTb).m_expected.m_harqProcessId << " for RNTI "
1554 << rnti);
1555 m_harqPhyModule->UpdateDlHarqProcessStatus(
1556 rnti,
1557 GetTBInfo(*itTb).m_expected.m_harqProcessId,
1558 GetTBInfo(*itTb).m_outputOfEM);
1559 }
1560 } // end if (itTb->second.downlink) HARQ
1561 } // end if (!itTb->second.harqFeedbackSent)
1562 }
1563 }
1564
1565 // forward control messages of this frame to NrPhy
1566
1567 if (!m_rxControlMessageList.empty() && m_phyRxCtrlEndOkCallback)
1568 {
1569 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1570 }
1571
1572 // if in unlicensed mode check after reception if the state should be
1573 // changed to IDLE or CCA_BUSY
1574 if (m_unlicensedMode)
1575 {
1576 MaybeCcaBusy();
1577 }
1578 else
1579 {
1580 ChangeState(IDLE, Seconds(0));
1581 }
1582
1583 m_rxPacketBurstList.clear();
1584 m_transportBlocks.clear();
1585 m_rxControlMessageList.clear();
1586}
1587
1588void
1589NrSpectrumPhy::EndRxCtrl()
1590{
1591 NS_LOG_FUNCTION(this);
1592 NS_ASSERT(m_state == RX_DL_CTRL || m_state == RX_UL_CTRL);
1593
1594 m_interferenceCtrl->EndRx();
1595
1596 // control error model not supported
1597 // forward control messages of this frame to LtePhy
1598 if (!m_rxControlMessageList.empty())
1599 {
1600 if (m_phyRxCtrlEndOkCallback)
1601 {
1602 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1603 }
1604 }
1605
1606 // if in unlicensed mode check after reception if we are in IDLE or CCA_BUSY mode
1607 if (m_unlicensedMode)
1608 {
1609 MaybeCcaBusy();
1610 }
1611 else
1612 {
1613 ChangeState(IDLE, Seconds(0));
1614 }
1615
1616 m_rxControlMessageList.clear();
1617}
1618
1619void
1620NrSpectrumPhy::EndRxSrs()
1621{
1622 NS_LOG_FUNCTION(this);
1623 NS_ASSERT(m_state == RX_UL_SRS && m_rxControlMessageList.size() == 1);
1624
1625 // notify interference calculator that the reception of SRS is finished,
1626 // so that chunk processors can be notified to calculate SINR, and if other
1627 // processor is registered
1628 m_interferenceSrs->EndRx();
1629
1630 if (m_phyRxCtrlEndOkCallback)
1631 {
1632 m_phyRxCtrlEndOkCallback(m_rxControlMessageList, GetBwpId());
1633 }
1634
1635 // if in unlicensed mode check after reception if we are in IDLE or CCA_BUSY mode
1636 if (m_unlicensedMode)
1637 {
1638 MaybeCcaBusy();
1639 }
1640 else
1641 {
1642 ChangeState(IDLE, Seconds(0));
1643 }
1644
1645 m_rxControlMessageList.clear();
1646}
1647
1648void
1649NrSpectrumPhy::MaybeCcaBusy()
1650{
1651 NS_LOG_FUNCTION(this);
1652 Time delayUntilCcaEnd = m_interferenceData->GetEnergyDuration(m_ccaMode1ThresholdW);
1653 if (!delayUntilCcaEnd.IsZero())
1654 {
1655 NS_LOG_DEBUG("Channel detected BUSY for:" << delayUntilCcaEnd << " ns.");
1656
1657 ChangeState(CCA_BUSY, delayUntilCcaEnd);
1658
1659 // check if with the new energy the channel will be for longer time in CCA_BUSY
1660 if (m_busyTimeEnds < Simulator::Now() + delayUntilCcaEnd)
1661 {
1662 m_busyTimeEnds = Simulator::Now() + delayUntilCcaEnd;
1663
1664 if (m_checkIfIsIdleEvent.IsPending())
1665 {
1666 m_checkIfIsIdleEvent.Cancel();
1667 }
1668
1669 NS_LOG_DEBUG("Check if still BUSY in:" << delayUntilCcaEnd
1670 << " us, and that is at "
1671 " time:"
1672 << Simulator::Now() + delayUntilCcaEnd
1673 << " and current time is:" << Simulator::Now());
1674
1675 m_checkIfIsIdleEvent =
1676 Simulator::Schedule(delayUntilCcaEnd, &NrSpectrumPhy::CheckIfStillBusy, this);
1677 }
1678 }
1679 else
1680 {
1681 NS_ABORT_MSG_IF(m_checkIfIsIdleEvent.IsPending(),
1682 "Unexpected state: returning to IDLE while there is an event "
1683 "running that should switch from CCA_BUSY to IDLE ?!");
1684 NS_LOG_DEBUG("Channel detected IDLE after being in: " << m_state << " state.");
1685 ChangeState(IDLE, Seconds(0));
1686 }
1687}
1688
1689void
1690NrSpectrumPhy::CheckIfStillBusy()
1691{
1692 NS_LOG_FUNCTION(this);
1693 NS_ABORT_MSG_IF(m_state == IDLE, "This function should not be called when in IDLE state.");
1694 // If in state of RX/TX do not switch to CCA_BUSY until RX/TX is finished.
1695 // When RX/TX finishes, check if the channel is still busy.
1696 if (m_state == CCA_BUSY)
1697 {
1698 MaybeCcaBusy();
1699 }
1700 else // RX_DL_CTRL, RX_UL_CTRL, RX_DATA, TX
1701 {
1702 Time delayUntilCcaEnd = m_interferenceData->GetEnergyDuration(m_ccaMode1ThresholdW);
1703
1704 if (delayUntilCcaEnd.IsZero())
1705 {
1706 NS_LOG_INFO(" Channel found IDLE as expected.");
1707 }
1708 else
1709 {
1710 NS_LOG_INFO(" Wait while channel BUSY for: " << delayUntilCcaEnd << " ns.");
1711 }
1712 }
1713}
1714
1715bool
1716NrSpectrumPhy::IsOnlySrs(const std::list<Ptr<NrControlMessage>>& ctrlMsgList)
1717{
1718 NS_ASSERT_MSG(!ctrlMsgList.empty(), "Passed an empty uplink control list");
1719
1720 return ctrlMsgList.size() == 1 &&
1721 (*ctrlMsgList.begin())->GetMessageType() == NrControlMessage::SRS;
1722}
1723
1724void
1726{
1727 NS_LOG_FUNCTION(this << dlDataSnr);
1728
1729 Ptr<NrUeNetDevice> ueNetDevice = DynamicCast<NrUeNetDevice>(GetDevice());
1730
1731 m_dlDataSnrTrace(m_phy->GetCurrentSfnSf(),
1732 m_phy->GetCellId(),
1733 m_phy->GetBwpId(),
1734 ueNetDevice->GetImsi(),
1735 dlDataSnr);
1736}
1737
1738int64_t
1740{
1741 NS_LOG_FUNCTION(this << stream);
1742 m_random->SetStream(stream);
1743 return 1;
1744}
1745
1746void
1747NrSpectrumPhy::UpdateMimoSinrPerceived(const std::vector<MimoSinrChunk>& mimoChunks)
1748{
1749 m_mimoSinrPerceived = mimoChunks;
1750}
1751
1752void
1753NrSpectrumPhy::AddDataMimoChunkProcessor(const Ptr<NrMimoChunkProcessor>& p)
1754{
1755 NS_LOG_FUNCTION(this);
1756 m_interferenceData->AddMimoChunkProcessor(p);
1757}
1758
1759} // namespace ns3
std::vector< Ptr< NrErrorModelOutput > > NrErrorModelHistory
Vector of previous output.
static TypeId GetTypeId()
GetTypeId.
const NrErrorModel::NrErrorModelHistory & GetHarqProcessInfoDl(uint16_t rnti, uint8_t harqProcId)
Return the info of the HARQ procId in case of retransmissions for DL (asynchronous)
const NrErrorModel::NrErrorModelHistory & GetHarqProcessInfoUl(uint16_t rnti, uint8_t harqProcId)
Return the info of the HARQ procId in case of retransmissions for UL (asynchronous)
static TypeId GetTypeId()
GetTypeId.
void SetUnlicensedMode(bool unlicensedMode)
Sets whether to perform in unclicensed mode in which the channel monitoring is enabled.
void SetErrorModelType(TypeId errorModelType)
Sets the error model type.
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.
void SetIsEnb(bool isEnb)
Set whether this spectrum PHY belongs to eNB or UE TODO NrHelper should be declared as friend and thi...
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< NrHarqPhy > GetHarqPhyModule() const
void InstallHarqPhyModule(const Ptr< NrHarqPhy > &harq)
Install HARQ phy module of this spectrum phy.
void AddSrsSnrReportCallback(SrsSnrReportCallback callback)
It adds callback to the list of callbacks that will be notified once SRS is being received.
void SetPhyDlHarqFeedbackCallback(const NrPhyDlHarqFeedbackCallback &c)
Sets the callback to be called when DL HARQ feedback is generated.
void AddDataSinrChunkProcessor(const Ptr< LteChunkProcessor > &p)
Adds the chunk processor that will process the interference.
Ptr< NrInterference > GetNrInterference() const
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
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 AddDlCtrlSinrChunkProcessor(const Ptr< LteChunkProcessor > &p)
Adds the chunk processor that will process the received power.
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.
double GetCcaMode1Threshold() const
Callback< void, const DlHarqInfo & > NrPhyDlHarqFeedbackCallback
void AddDataPowerChunkProcessor(const Ptr< LteChunkProcessor > &p)
Adds the chunk processor that will process the power for the data.
int64_t AssignStreams(int64_t stream)
Ptr< SpectrumChannel > GetSpectrumChannel() const
Returns spectrum channel object to which is attached this spectrum phy instance.
uint16_t GetCellId() const
Callback< void, uint16_t, const Ptr< SpectrumValue > & > NrPhyRxPssCallback
void SetDataErrorModelEnabled(bool dataErrorModelEnabled)
Enables or disabled data error model.
void AddExpectedTb(uint16_t rnti, uint8_t ndi, uint32_t size, uint8_t mcs, uint8_t rank, const std::vector< int > &rbMap, uint8_t harqId, uint8_t rv, bool downlink, uint8_t symStart, uint8_t numSym, const SfnSf &sfn)
Instruct the Spectrum Model of a incoming transmission.
void SetPhyRxDataEndOkCallback(const NrPhyRxDataEndOkCallback &c)
Sets the callback to be called when DATA is received successfully.
void AddRsPowerChunkProcessor(const Ptr< LteChunkProcessor > &p)
Adds the chunk processor that will process the received power.
void ReportDlCtrlSinr(const SpectrumValue &sinr)
Called when DlCtrlSinr is fired.
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 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.
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)
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 ...
Ptr< Object > GetAntenna() const override
Inherited from SpectrumPhy Note: Implements GetAntenna function from SpectrumPhy.
void SetPhyRxPssCallback(const NrPhyRxPssCallback &c)
The SfnSf class.
Definition sfnsf.h:34
enum ns3::DlHarqInfo::HarqStatus NACK
HARQ status.
The GnbPhyPacketCountParameter struct.