5G-LENA nr-v4.0
The 5G/NR module for the ns-3 simulator
Loading...
Searching...
No Matches
nr-ue-rrc.cc
1// Copyright (c) 2011, 2012 Centre Tecnologic de Telecomunicacions de Catalunya (CTTC)
2// Copyright (c) 2018 Fraunhofer ESK : RLF extensions
3//
4// SPDX-License-Identifier: GPL-2.0-only
5//
6// Author: Nicola Baldo <nbaldo@cttc.es>
7// Budiarto Herman <budiarto.herman@magister.fi>
8// Modified by:
9// Danilo Abrignani <danilo.abrignani@unibo.it> (Carrier Aggregation - GSoC 2015)
10// Biljana Bojovic <biljana.bojovic@cttc.es> (Carrier Aggregation)
11// Vignesh Babu <ns3-dev@esk.fraunhofer.de> (RLF extensions)
12
13#include "nr-ue-rrc.h"
14
15#include "nr-common.h"
16#include "nr-pdcp.h"
17#include "nr-radio-bearer-info.h"
18#include "nr-rlc-am.h"
19#include "nr-rlc-tm.h"
20#include "nr-rlc-um.h"
21#include "nr-rlc.h"
22
23#include "ns3/fatal-error.h"
24#include "ns3/log.h"
25#include "ns3/object-factory.h"
26#include "ns3/object-map.h"
27#include "ns3/simulator.h"
28
29#include <cmath>
30
31namespace ns3
32{
33const Time NR_UE_MEASUREMENT_REPORT_DELAY = MicroSeconds(1);
34
35NS_LOG_COMPONENT_DEFINE("NrUeRrc");
36
38// CMAC SAP forwarder
40
42class UeMemberNrUeCmacSapUser : public NrUeCmacSapUser
43{
44 public:
50 UeMemberNrUeCmacSapUser(NrUeRrc* rrc);
51
52 void SetTemporaryCellRnti(uint16_t rnti) override;
53 void NotifyRandomAccessSuccessful() override;
54 void NotifyRandomAccessFailed() override;
55
56 private:
57 NrUeRrc* m_rrc;
58};
59
60UeMemberNrUeCmacSapUser::UeMemberNrUeCmacSapUser(NrUeRrc* rrc)
61 : m_rrc(rrc)
62{
63}
64
65void
66UeMemberNrUeCmacSapUser::SetTemporaryCellRnti(uint16_t rnti)
67{
68 m_rrc->DoSetTemporaryCellRnti(rnti);
69}
70
71void
72UeMemberNrUeCmacSapUser::NotifyRandomAccessSuccessful()
73{
74 m_rrc->DoNotifyRandomAccessSuccessful();
75}
76
77void
78UeMemberNrUeCmacSapUser::NotifyRandomAccessFailed()
79{
80 m_rrc->DoNotifyRandomAccessFailed();
81}
82
84static const std::string g_ueRrcStateName[NrUeRrc::NUM_STATES] = {
85 "IDLE_START",
86 "IDLE_CELL_SEARCH",
87 "IDLE_WAIT_MIB_SIB1",
88 "IDLE_WAIT_MIB",
89 "IDLE_WAIT_SIB1",
90 "IDLE_CAMPED_NORMALLY",
91 "IDLE_WAIT_SIB2",
92 "IDLE_RANDOM_ACCESS",
93 "IDLE_CONNECTING",
94 "CONNECTED_NORMALLY",
95 "CONNECTED_HANDOVER",
96 "CONNECTED_PHY_PROBLEM",
97 "CONNECTED_REESTABLISHING",
98};
99
101// ue RRC methods
103
104NS_OBJECT_ENSURE_REGISTERED(NrUeRrc);
105
106NrUeRrc::NrUeRrc()
107 : m_cmacSapProvider(0),
108 m_rrcSapUser(nullptr),
109 m_macSapProvider(nullptr),
110 m_asSapUser(nullptr),
111 m_ccmRrcSapProvider(nullptr),
112 m_state(IDLE_START),
113 m_imsi(0),
114 m_rnti(0),
115 m_cellId(0),
116 m_useRlcSm(true),
117 m_connectionPending(false),
118 m_hasReceivedMib(false),
119 m_hasReceivedSib1(false),
120 m_hasReceivedSib2(false),
121 m_csgWhiteList(0),
122 m_noOfSyncIndications(0),
123 m_leaveConnectedMode(false),
124 m_previousCellId(0),
125 m_connEstFailCountLimit(0),
126 m_connEstFailCount(0),
127 m_numberOfComponentCarriers(nr::MIN_NO_CC)
128{
129 NS_LOG_FUNCTION(this);
130 m_cphySapUser.push_back(new MemberNrUeCphySapUser<NrUeRrc>(this));
131 m_cmacSapUser.push_back(new UeMemberNrUeCmacSapUser(this));
132 m_cphySapProvider.push_back(nullptr);
133 m_cmacSapProvider.push_back(nullptr);
134 m_rrcSapProvider = new MemberNrUeRrcSapProvider<NrUeRrc>(this);
135 m_drbPdcpSapUser = new NrPdcpSpecificNrPdcpSapUser<NrUeRrc>(this);
136 m_asSapProvider = new MemberNrAsSapProvider<NrUeRrc>(this);
137 m_ccmRrcSapUser = new MemberNrUeCcmRrcSapUser<NrUeRrc>(this);
138}
139
141{
142 NS_LOG_FUNCTION(this);
143}
144
145void
146NrUeRrc::DoDispose()
147{
148 NS_LOG_FUNCTION(this);
149 for (uint16_t i = 0; i < m_numberOfComponentCarriers; i++)
150 {
151 delete m_cphySapUser.at(i);
152 delete m_cmacSapUser.at(i);
153 }
154 m_cphySapUser.clear();
155 m_cmacSapUser.clear();
156 delete m_rrcSapProvider;
157 delete m_drbPdcpSapUser;
158 delete m_asSapProvider;
159 delete m_ccmRrcSapUser;
160 m_cphySapProvider.erase(m_cphySapProvider.begin(), m_cphySapProvider.end());
161 m_cphySapProvider.clear();
162 m_cmacSapProvider.erase(m_cmacSapProvider.begin(), m_cmacSapProvider.end());
163 m_cmacSapProvider.clear();
164 m_drbMap.clear();
165}
166
167TypeId
169{
170 static TypeId tid =
171 TypeId("ns3::NrUeRrc")
172 .SetParent<Object>()
173 .SetGroupName("Nr")
174 .AddConstructor<NrUeRrc>()
175 .AddAttribute("DataRadioBearerMap",
176 "List of UE RadioBearerInfo for Data Radio Bearers by LCID.",
177 ObjectMapValue(),
178 MakeObjectMapAccessor(&NrUeRrc::m_drbMap),
179 MakeObjectMapChecker<NrDataRadioBearerInfo>())
180 .AddAttribute("Srb0",
181 "SignalingRadioBearerInfo for SRB0",
182 PointerValue(),
183 MakePointerAccessor(&NrUeRrc::m_srb0),
184 MakePointerChecker<NrSignalingRadioBearerInfo>())
185 .AddAttribute("Srb1",
186 "SignalingRadioBearerInfo for SRB1",
187 PointerValue(),
188 MakePointerAccessor(&NrUeRrc::m_srb1),
189 MakePointerChecker<NrSignalingRadioBearerInfo>())
190 .AddAttribute("CellId",
191 "Serving cell identifier",
192 UintegerValue(0), // unused, read-only attribute
193 MakeUintegerAccessor(&NrUeRrc::GetCellId),
194 MakeUintegerChecker<uint16_t>())
195 .AddAttribute("C-RNTI",
196 "Cell Radio Network Temporary Identifier",
197 UintegerValue(0), // unused, read-only attribute
198 MakeUintegerAccessor(&NrUeRrc::GetRnti),
199 MakeUintegerChecker<uint16_t>())
200 .AddAttribute(
201 "T300",
202 "Timer for the RRC Connection Establishment procedure "
203 "(i.e., the procedure is deemed as failed if it takes longer than this). "
204 "Standard values: 100ms, 200ms, 300ms, 400ms, 600ms, 1000ms, 1500ms, 2000ms",
205 TimeValue(MilliSeconds(
206 100)), // see 3GPP 36.331 UE-TimersAndConstants & RLF-TimersAndConstants
207 MakeTimeAccessor(&NrUeRrc::m_t300),
208 MakeTimeChecker(MilliSeconds(100), MilliSeconds(2000)))
209 .AddAttribute(
210 "T310",
211 "Timer for detecting the Radio link failure "
212 "(i.e., the radio link is deemed as failed if this timer expires). "
213 "Standard values: 0ms 50ms, 100ms, 200ms, 500ms, 1000ms, 2000ms",
214 TimeValue(MilliSeconds(
215 1000)), // see 3GPP 36.331 UE-TimersAndConstants & RLF-TimersAndConstants
216 MakeTimeAccessor(&NrUeRrc::m_t310),
217 MakeTimeChecker(MilliSeconds(0), MilliSeconds(2000)))
218 .AddAttribute(
219 "N310",
220 "This specifies the maximum number of out-of-sync indications. "
221 "Standard values: 1, 2, 3, 4, 6, 8, 10, 20",
222 UintegerValue(6), // see 3GPP 36.331 UE-TimersAndConstants & RLF-TimersAndConstants
223 MakeUintegerAccessor(&NrUeRrc::m_n310),
224 MakeUintegerChecker<uint8_t>(1, 20))
225 .AddAttribute(
226 "N311",
227 "This specifies the maximum number of in-sync indications. "
228 "Standard values: 1, 2, 3, 4, 5, 6, 8, 10",
229 UintegerValue(2), // see 3GPP 36.331 UE-TimersAndConstants & RLF-TimersAndConstants
230 MakeUintegerAccessor(&NrUeRrc::m_n311),
231 MakeUintegerChecker<uint8_t>(1, 10))
232 .AddTraceSource("MibReceived",
233 "trace fired upon reception of Master Information Block",
234 MakeTraceSourceAccessor(&NrUeRrc::m_mibReceivedTrace),
235 "ns3::NrUeRrc::MibSibHandoverTracedCallback")
236 .AddTraceSource("Sib1Received",
237 "trace fired upon reception of System Information Block Type 1",
238 MakeTraceSourceAccessor(&NrUeRrc::m_sib1ReceivedTrace),
239 "ns3::NrUeRrc::MibSibHandoverTracedCallback")
240 .AddTraceSource("Sib2Received",
241 "trace fired upon reception of System Information Block Type 2",
242 MakeTraceSourceAccessor(&NrUeRrc::m_sib2ReceivedTrace),
243 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
244 .AddTraceSource("StateTransition",
245 "trace fired upon every UE RRC state transition",
246 MakeTraceSourceAccessor(&NrUeRrc::m_stateTransitionTrace),
247 "ns3::NrUeRrc::StateTracedCallback")
248 .AddTraceSource("InitialCellSelectionEndOk",
249 "trace fired upon successful initial cell selection procedure",
250 MakeTraceSourceAccessor(&NrUeRrc::m_initialCellSelectionEndOkTrace),
251 "ns3::NrUeRrc::CellSelectionTracedCallback")
252 .AddTraceSource("InitialCellSelectionEndError",
253 "trace fired upon failed initial cell selection procedure",
254 MakeTraceSourceAccessor(&NrUeRrc::m_initialCellSelectionEndErrorTrace),
255 "ns3::NrUeRrc::CellSelectionTracedCallback")
256 .AddTraceSource("RandomAccessSuccessful",
257 "trace fired upon successful completion of the random access procedure",
258 MakeTraceSourceAccessor(&NrUeRrc::m_randomAccessSuccessfulTrace),
259 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
260 .AddTraceSource("RandomAccessError",
261 "trace fired upon failure of the random access procedure",
262 MakeTraceSourceAccessor(&NrUeRrc::m_randomAccessErrorTrace),
263 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
264 .AddTraceSource("ConnectionEstablished",
265 "trace fired upon successful RRC connection establishment",
266 MakeTraceSourceAccessor(&NrUeRrc::m_connectionEstablishedTrace),
267 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
268 .AddTraceSource("ConnectionTimeout",
269 "trace fired upon timeout RRC connection establishment because of T300",
270 MakeTraceSourceAccessor(&NrUeRrc::m_connectionTimeoutTrace),
271 "ns3::NrUeRrc::ImsiCidRntiCountTracedCallback")
272 .AddTraceSource("ConnectionReconfiguration",
273 "trace fired upon RRC connection reconfiguration",
274 MakeTraceSourceAccessor(&NrUeRrc::m_connectionReconfigurationTrace),
275 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
276 .AddTraceSource("HandoverStart",
277 "trace fired upon start of a handover procedure",
278 MakeTraceSourceAccessor(&NrUeRrc::m_handoverStartTrace),
279 "ns3::NrUeRrc::MibSibHandoverTracedCallback")
280 .AddTraceSource("HandoverEndOk",
281 "trace fired upon successful termination of a handover procedure",
282 MakeTraceSourceAccessor(&NrUeRrc::m_handoverEndOkTrace),
283 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
284 .AddTraceSource("HandoverEndError",
285 "trace fired upon failure of a handover procedure",
286 MakeTraceSourceAccessor(&NrUeRrc::m_handoverEndErrorTrace),
287 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
288 .AddTraceSource("SCarrierConfigured",
289 "trace fired after configuring secondary carriers",
290 MakeTraceSourceAccessor(&NrUeRrc::m_sCarrierConfiguredTrace),
291 "ns3::NrUeRrc::SCarrierConfiguredTracedCallback")
292 .AddTraceSource("Srb1Created",
293 "trace fired after SRB1 is created",
294 MakeTraceSourceAccessor(&NrUeRrc::m_srb1CreatedTrace),
295 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
296 .AddTraceSource("DrbCreated",
297 "trace fired after DRB is created",
298 MakeTraceSourceAccessor(&NrUeRrc::m_drbCreatedTrace),
299 "ns3::NrUeRrc::ImsiCidRntiLcIdTracedCallback")
300 .AddTraceSource("RadioLinkFailure",
301 "trace fired upon failure of radio link",
302 MakeTraceSourceAccessor(&NrUeRrc::m_radioLinkFailureTrace),
303 "ns3::NrUeRrc::ImsiCidRntiTracedCallback")
304 .AddTraceSource(
305 "PhySyncDetection",
306 "trace fired upon receiving in Sync or out of Sync indications from UE PHY",
307 MakeTraceSourceAccessor(&NrUeRrc::m_phySyncDetectionTrace),
308 "ns3::NrUeRrc::PhySyncDetectionTracedCallback");
309 return tid;
310}
311
312void
314{
315 NS_LOG_FUNCTION(this << s);
316 m_cphySapProvider.at(GetPrimaryDlIndex()) = s;
317}
318
319void
321{
322 NS_LOG_FUNCTION(this << s);
323 m_cphySapProvider.at(index) = s;
324}
325
328{
329 NS_LOG_FUNCTION(this);
330 return m_cphySapUser.at(GetPrimaryDlIndex());
331}
332
335{
336 NS_LOG_FUNCTION(this);
337 return m_cphySapUser.at(index);
338}
339
340void
342{
343 NS_LOG_FUNCTION(this << s);
344 m_cmacSapProvider.at(GetPrimaryDlIndex()) = s;
345}
346
347void
349{
350 NS_LOG_FUNCTION(this << s);
351 m_cmacSapProvider.at(index) = s;
352}
353
356{
357 NS_LOG_FUNCTION(this);
358 return m_cmacSapUser.at(GetPrimaryDlIndex());
359}
360
363{
364 NS_LOG_FUNCTION(this);
365 return m_cmacSapUser.at(index);
366}
367
368void
370{
371 NS_LOG_FUNCTION(this << s);
372 m_rrcSapUser = s;
373}
374
377{
378 NS_LOG_FUNCTION(this);
379 return m_rrcSapProvider;
380}
381
382void
384{
385 NS_LOG_FUNCTION(this << s);
386 m_macSapProvider = s;
387}
388
389void
391{
392 NS_LOG_FUNCTION(this << s);
393 m_ccmRrcSapProvider = s;
394}
395
398{
399 NS_LOG_FUNCTION(this);
400 return m_ccmRrcSapUser;
401}
402
403void
405{
406 m_asSapUser = s;
407}
408
411{
412 return m_asSapProvider;
413}
414
415void
416NrUeRrc::SetImsi(uint64_t imsi)
417{
418 NS_LOG_FUNCTION(this << imsi);
419 m_imsi = imsi;
420
421 // Communicate the IMSI to MACs and PHYs for all the component carriers
422 for (uint16_t i = 0; i < m_numberOfComponentCarriers; i++)
423 {
424 m_cmacSapProvider.at(i)->SetImsi(m_imsi);
425 m_cphySapProvider.at(i)->SetImsi(m_imsi);
426 }
427}
428
429void
431{
432 NS_LOG_FUNCTION(this << cellId);
433 m_previousCellId = cellId;
434}
435
436uint64_t
438{
439 return m_imsi;
440}
441
442uint16_t
444{
445 NS_LOG_FUNCTION(this);
446 return m_rnti;
447}
448
449uint16_t
451{
452 NS_LOG_FUNCTION(this);
453 return m_cellId;
454}
455
456bool
457NrUeRrc::IsServingCell(uint16_t cellId) const
458{
459 NS_LOG_FUNCTION(this);
460 for (auto& cphySap : m_cphySapProvider)
461 {
462 if (cellId == cphySap->GetCellId())
463 {
464 return true;
465 }
466 }
467 return false;
468}
469
470uint8_t
472{
473 NS_LOG_FUNCTION(this);
474 return m_ulBandwidth;
475}
476
477uint8_t
479{
480 NS_LOG_FUNCTION(this);
481 return m_dlBandwidth;
482}
483
484uint32_t
486{
487 return m_dlEarfcn;
488}
489
490uint32_t
492{
493 NS_LOG_FUNCTION(this);
494 return m_ulEarfcn;
495}
496
499{
500 NS_LOG_FUNCTION(this);
501 return m_state;
502}
503
504uint16_t
506{
507 NS_LOG_FUNCTION(this);
508 return m_previousCellId;
509}
510
511void
513{
514 NS_LOG_FUNCTION(this);
515 m_useRlcSm = val;
516}
517
518void
520{
521 m_primaryUlIndex = ulIndex;
522}
523
524uint16_t
526{
527 return m_primaryUlIndex;
528}
529
530void
532{
533 m_primaryDlIndex = dlIndex;
534}
535
536uint16_t
538{
539 return m_primaryDlIndex;
540}
541
542void
544{
545 NS_LOG_FUNCTION(this);
546
547 // setup the UE side of SRB0
548 uint8_t lcid = 0;
549
550 Ptr<NrRlc> rlc = CreateObject<NrRlcTm>()->GetObject<NrRlc>();
551 rlc->SetNrMacSapProvider(m_macSapProvider);
552 rlc->SetRnti(m_rnti);
553 rlc->SetLcId(lcid);
554
555 m_srb0 = CreateObject<NrSignalingRadioBearerInfo>();
556 m_srb0->m_rlc = rlc;
557 m_srb0->m_srbIdentity = 0;
559 ueParams.srb0SapProvider = m_srb0->m_rlc->GetNrRlcSapProvider();
560 ueParams.srb1SapProvider = nullptr;
561 m_rrcSapUser->Setup(ueParams);
562
563 // CCCH (LCID 0) is pre-configured, here is the hardcoded configuration:
565 lcConfig.priority = 0; // highest priority
566 lcConfig.prioritizedBitRateKbps = 65535; // maximum
567 lcConfig.bucketSizeDurationMs = 65535; // maximum
568 lcConfig.logicalChannelGroup = 0; // all SRBs mapped to LCG 0
569 NrMacSapUser* msu =
570 m_ccmRrcSapProvider->ConfigureSignalBearer(lcid, lcConfig, rlc->GetNrMacSapUser());
571 m_cmacSapProvider.at(GetPrimaryUlIndex())->AddLc(lcid, lcConfig, msu);
572}
573
574void
576{
577 NS_LOG_FUNCTION(this);
578 if (m_numberOfComponentCarriers < nr::MIN_NO_CC || m_numberOfComponentCarriers > nr::MAX_NO_CC)
579 {
580 // this check is needed in order to maintain backward compatibility with scripts and tests
581 // if case lte-helper is not used (like in several tests) the m_numberOfComponentCarriers
582 // is not set and then an error is raised
583 // In this case m_numberOfComponentCarriers is set to 1
584 m_numberOfComponentCarriers = nr::MIN_NO_CC;
585 }
586 if (m_numberOfComponentCarriers > nr::MIN_NO_CC)
587 {
588 for (uint16_t i = 1; i < m_numberOfComponentCarriers; i++)
589 {
590 m_cphySapUser.push_back(new MemberNrUeCphySapUser<NrUeRrc>(this));
591 m_cmacSapUser.push_back(new UeMemberNrUeCmacSapUser(this));
592 m_cphySapProvider.push_back(nullptr);
593 m_cmacSapProvider.push_back(nullptr);
594 }
595 }
596}
597
598void
599NrUeRrc::DoSendData(Ptr<Packet> packet, uint8_t bid)
600{
601 NS_LOG_FUNCTION(this << packet);
602
603 uint8_t drbid = Bid2Drbid(bid);
604
605 if (drbid != 0)
606 {
607 auto it = m_drbMap.find(drbid);
608 NS_ASSERT_MSG(it != m_drbMap.end(), "could not find bearer with drbid == " << drbid);
609
611 params.pdcpSdu = packet;
612 params.rnti = m_rnti;
613 params.lcid = it->second->m_logicalChannelIdentity;
614
615 NS_LOG_LOGIC(this << " RNTI=" << m_rnti << " sending packet " << packet << " on DRBID "
616 << (uint32_t)drbid << " (LCID " << (uint32_t)params.lcid << ")"
617 << " (" << packet->GetSize() << " bytes)");
618 it->second->m_pdcp->GetNrPdcpSapProvider()->TransmitPdcpSdu(params);
619 }
620}
621
622void
623NrUeRrc::DoDisconnect()
624{
625 NS_LOG_FUNCTION(this);
626
627 switch (m_state)
628 {
629 case IDLE_START:
630 case IDLE_CELL_SEARCH:
631 case IDLE_WAIT_MIB_SIB1:
632 case IDLE_WAIT_MIB:
633 case IDLE_WAIT_SIB1:
634 case IDLE_CAMPED_NORMALLY:
635 NS_LOG_INFO("already disconnected");
636 break;
637
638 case IDLE_WAIT_SIB2:
639 case IDLE_CONNECTING:
640 NS_FATAL_ERROR("cannot abort connection setup procedure");
641 break;
642
643 case CONNECTED_NORMALLY:
644 case CONNECTED_HANDOVER:
645 case CONNECTED_PHY_PROBLEM:
646 case CONNECTED_REESTABLISHING:
647 LeaveConnectedMode();
648 break;
649
650 default: // i.e. IDLE_RANDOM_ACCESS
651 NS_FATAL_ERROR("method unexpected in state " << ToString(m_state));
652 break;
653 }
654}
655
656void
657NrUeRrc::DoReceivePdcpSdu(NrPdcpSapUser::ReceivePdcpSduParameters params)
658{
659 NS_LOG_FUNCTION(this);
660 m_asSapUser->RecvData(params.pdcpSdu);
661}
662
663void
664NrUeRrc::DoSetTemporaryCellRnti(uint16_t rnti)
665{
666 NS_LOG_FUNCTION(this << rnti);
667 m_rnti = rnti;
668 m_srb0->m_rlc->SetRnti(m_rnti);
669 m_cphySapProvider.at(GetPrimaryUlIndex())->SetRnti(m_rnti);
670 m_cphySapProvider.at(GetPrimaryDlIndex())->SetRnti(m_rnti);
671 m_cmacSapProvider.at(GetPrimaryUlIndex())->SetRnti(m_rnti);
672 m_cmacSapProvider.at(GetPrimaryDlIndex())->SetRnti(m_rnti);
673}
674
675void
676NrUeRrc::DoNotifyRandomAccessSuccessful()
677{
678 NS_LOG_FUNCTION(this << m_imsi << ToString(m_state));
679 m_randomAccessSuccessfulTrace(m_imsi, m_cellId, m_rnti);
680
681 switch (m_state)
682 {
683 case IDLE_RANDOM_ACCESS: {
684 // we just received a RAR with a T-C-RNTI and an UL grant
685 // send RRC connection request as message 3 of the random access procedure
686 SwitchToState(IDLE_CONNECTING);
687 NrRrcSap::RrcConnectionRequest msg;
688 msg.ueIdentity = m_imsi;
689 m_rrcSapUser->SendRrcConnectionRequest(msg);
690 m_connectionTimeout = Simulator::Schedule(m_t300, &NrUeRrc::ConnectionTimeout, this);
691 }
692 break;
693
694 case CONNECTED_HANDOVER: {
695 NrRrcSap::RrcConnectionReconfigurationCompleted msg;
696 msg.rrcTransactionIdentifier = m_lastRrcTransactionIdentifier;
698
699 // 3GPP TS 36.331 section 5.5.6.1 Measurements related actions upon handover
700 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
701 measIdIt != m_varMeasConfig.measIdList.end();
702 ++measIdIt)
703 {
704 VarMeasReportListClear(measIdIt->second.measId);
705 }
706
707 SwitchToState(CONNECTED_NORMALLY);
708 m_cmacSapProvider.at(GetPrimaryUlIndex())
709 ->NotifyConnectionSuccessful(); // RA successful during handover
710 m_handoverEndOkTrace(m_imsi, m_cellId, m_rnti);
711 }
712 break;
713
714 default:
715 NS_FATAL_ERROR("unexpected event in state " << ToString(m_state));
716 break;
717 }
718}
719
720void
721NrUeRrc::DoNotifyRandomAccessFailed()
722{
723 NS_LOG_FUNCTION(this << m_imsi << ToString(m_state));
724 m_randomAccessErrorTrace(m_imsi, m_cellId, m_rnti);
725
726 switch (m_state)
727 {
728 case IDLE_RANDOM_ACCESS: {
729 SwitchToState(IDLE_CAMPED_NORMALLY);
730 m_asSapUser->NotifyConnectionFailed();
731 }
732 break;
733
734 case CONNECTED_HANDOVER: {
735 m_handoverEndErrorTrace(m_imsi, m_cellId, m_rnti);
741 if (!m_leaveConnectedMode)
742 {
743 m_leaveConnectedMode = true;
744 SwitchToState(CONNECTED_PHY_PROBLEM);
745 m_rrcSapUser->SendIdealUeContextRemoveRequest(m_rnti);
746 // we should have called NotifyConnectionFailed
747 // but that method would immediately ask you UE to
748 // connect rather than doing cell selection again.
749 m_asSapUser->NotifyConnectionReleased();
750 }
751 }
752 break;
753
754 default:
755 NS_FATAL_ERROR("unexpected event in state " << ToString(m_state));
756 break;
757 }
758}
759
760void
761NrUeRrc::DoSetCsgWhiteList(uint32_t csgId)
762{
763 NS_LOG_FUNCTION(this << m_imsi << csgId);
764 m_csgWhiteList = csgId;
765}
766
767void
768NrUeRrc::DoStartCellSelection(uint32_t dlEarfcn)
769{
770 NS_LOG_FUNCTION(this << m_imsi << dlEarfcn);
771 NS_ASSERT_MSG(m_state == IDLE_START,
772 "cannot start cell selection from state " << ToString(m_state));
773 m_dlEarfcn = dlEarfcn;
774 m_cphySapProvider.at(GetPrimaryDlIndex())->StartCellSearch(dlEarfcn);
775 SwitchToState(IDLE_CELL_SEARCH);
776}
777
778void
779NrUeRrc::DoForceCampedOnGnb(uint16_t cellId, uint32_t dlEarfcn)
780{
781 NS_LOG_FUNCTION(this << m_imsi << cellId << dlEarfcn);
782
783 switch (m_state)
784 {
785 case IDLE_START:
786 m_cellId = cellId;
787 m_dlEarfcn = dlEarfcn;
788 m_cphySapProvider.at(GetPrimaryDlIndex())->SynchronizeWithGnb(m_cellId, m_dlEarfcn);
789 SwitchToState(IDLE_WAIT_MIB);
790 break;
791
792 case IDLE_CELL_SEARCH:
793 case IDLE_WAIT_MIB_SIB1:
794 case IDLE_WAIT_SIB1:
795 NS_FATAL_ERROR("cannot abort cell selection " << ToString(m_state));
796 break;
797
798 case IDLE_WAIT_MIB:
799 NS_LOG_INFO("already forced to camp to cell " << m_cellId);
800 break;
801
802 case IDLE_CAMPED_NORMALLY:
803 case IDLE_WAIT_SIB2:
804 case IDLE_RANDOM_ACCESS:
805 case IDLE_CONNECTING:
806 NS_LOG_INFO("already camped to cell " << m_cellId);
807 break;
808
809 case CONNECTED_NORMALLY:
810 case CONNECTED_HANDOVER:
811 case CONNECTED_PHY_PROBLEM:
812 case CONNECTED_REESTABLISHING:
813 NS_LOG_INFO("already connected to cell " << m_cellId);
814 break;
815
816 default:
817 NS_FATAL_ERROR("unexpected event in state " << ToString(m_state));
818 break;
819 }
820}
821
822void
823NrUeRrc::DoConnect()
824{
825 NS_LOG_FUNCTION(this << m_imsi);
826
827 switch (m_state)
828 {
829 case IDLE_START:
830 case IDLE_CELL_SEARCH:
831 case IDLE_WAIT_MIB_SIB1:
832 case IDLE_WAIT_SIB1:
833 case IDLE_WAIT_MIB:
834 m_connectionPending = true;
835 break;
836
837 case IDLE_CAMPED_NORMALLY:
838 m_connectionPending = true;
839 SwitchToState(IDLE_WAIT_SIB2);
840 break;
841
842 case IDLE_WAIT_SIB2:
843 case IDLE_RANDOM_ACCESS:
844 case IDLE_CONNECTING:
845 NS_LOG_INFO("already connecting");
846 break;
847
848 case CONNECTED_NORMALLY:
849 case CONNECTED_REESTABLISHING:
850 case CONNECTED_HANDOVER:
851 NS_LOG_INFO("already connected");
852 break;
853
854 default:
855 NS_FATAL_ERROR("unexpected event in state " << ToString(m_state));
856 break;
857 }
858}
859
860// CPHY SAP methods
861
862void
863NrUeRrc::DoRecvMasterInformationBlock(uint16_t cellId, NrRrcSap::MasterInformationBlock msg)
864{
865 m_dlBandwidth = msg.dlBandwidth;
866 m_cphySapProvider.at(GetPrimaryDlIndex())->SetDlBandwidth(msg.dlBandwidth);
867 m_cphySapProvider.at(GetPrimaryDlIndex())->SetNumerology(msg.numerology);
868 m_cphySapProvider.at(GetPrimaryUlIndex())->SetNumerology(msg.numerology);
869 m_hasReceivedMib = true;
870 m_mibReceivedTrace(m_imsi, m_cellId, m_rnti, cellId);
871
872 switch (m_state)
873 {
874 case IDLE_WAIT_MIB:
875 // manual attachment
876 SwitchToState(IDLE_CAMPED_NORMALLY);
877 break;
878
879 case IDLE_WAIT_MIB_SIB1:
880 // automatic attachment from Idle mode cell selection
881 SwitchToState(IDLE_WAIT_SIB1);
882 break;
883
884 default:
885 // do nothing extra
886 break;
887 }
888}
889
890void
891NrUeRrc::DoRecvSystemInformationBlockType1(uint16_t cellId,
892 NrRrcSap::SystemInformationBlockType1 msg)
893{
894 NS_LOG_FUNCTION(this);
895 switch (m_state)
896 {
897 case IDLE_WAIT_SIB1:
898 NS_ASSERT_MSG(cellId == msg.cellAccessRelatedInfo.cellIdentity,
899 "Cell identity in SIB1 does not match with the originating cell");
900 m_hasReceivedSib1 = true;
901 m_lastSib1 = msg;
902 m_sib1ReceivedTrace(m_imsi, m_cellId, m_rnti, cellId);
903 EvaluateCellForSelection();
904 break;
905
906 case IDLE_CAMPED_NORMALLY:
907 case IDLE_RANDOM_ACCESS:
908 case IDLE_CONNECTING:
909 case CONNECTED_NORMALLY:
910 case CONNECTED_HANDOVER:
911 case CONNECTED_PHY_PROBLEM:
912 case CONNECTED_REESTABLISHING:
913 NS_ASSERT_MSG(cellId == msg.cellAccessRelatedInfo.cellIdentity,
914 "Cell identity in SIB1 does not match with the originating cell");
915 m_hasReceivedSib1 = true;
916 m_lastSib1 = msg;
917 m_sib1ReceivedTrace(m_imsi, m_cellId, m_rnti, cellId);
918 break;
919
920 case IDLE_WAIT_MIB_SIB1:
921 // MIB has not been received, so ignore this SIB1
922
923 default: // e.g. IDLE_START, IDLE_CELL_SEARCH, IDLE_WAIT_MIB, IDLE_WAIT_SIB2
924 // do nothing
925 break;
926 }
927}
928
929void
930NrUeRrc::DoReportUeMeasurements(NrUeCphySapUser::UeMeasurementsParameters params)
931{
932 NS_LOG_FUNCTION(this);
933
934 // layer 3 filtering does not apply in IDLE mode
935 bool useLayer3Filtering = (m_state == CONNECTED_NORMALLY);
936 bool triggering = true;
937 for (auto newMeasIt = params.m_ueMeasurementsList.begin();
938 newMeasIt != params.m_ueMeasurementsList.end();
939 ++newMeasIt)
940 {
941 if (params.m_componentCarrierId != 0)
942 {
943 triggering = false; // report is triggered only when an event is on the primary carrier
944 // in this case the measurement received is related to secondary carriers
945 }
946 SaveUeMeasurements(newMeasIt->m_cellId,
947 newMeasIt->m_rsrp,
948 newMeasIt->m_rsrq,
949 useLayer3Filtering,
950 params.m_componentCarrierId);
951 }
952
953 if (m_state == IDLE_CELL_SEARCH)
954 {
955 // start decoding BCH
956 SynchronizeToStrongestCell();
957 }
958 else
959 {
960 if (triggering)
961 {
962 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
963 measIdIt != m_varMeasConfig.measIdList.end();
964 ++measIdIt)
965 {
966 MeasurementReportTriggering(measIdIt->first);
967 }
968 }
969 }
970
971} // end of NrUeRrc::DoReportUeMeasurements
972
973// RRC SAP methods
974
975void
976NrUeRrc::DoCompleteSetup(NrUeRrcSapProvider::CompleteSetupParameters params)
977{
978 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
979 m_srb0->m_rlc->SetNrRlcSapUser(params.srb0SapUser);
980 if (m_srb1)
981 {
982 m_srb1->m_pdcp->SetNrPdcpSapUser(params.srb1SapUser);
983 }
984}
985
986void
987NrUeRrc::DoRecvSystemInformation(NrRrcSap::SystemInformation msg)
988{
989 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
990
991 if (msg.haveSib2)
992 {
993 switch (m_state)
994 {
995 case IDLE_CAMPED_NORMALLY:
996 case IDLE_WAIT_SIB2:
997 case IDLE_RANDOM_ACCESS:
998 case IDLE_CONNECTING:
999 case CONNECTED_NORMALLY:
1000 case CONNECTED_HANDOVER:
1001 case CONNECTED_PHY_PROBLEM:
1002 case CONNECTED_REESTABLISHING:
1003 m_hasReceivedSib2 = true;
1004 m_ulBandwidth = msg.sib2.freqInfo.ulBandwidth;
1005 m_ulEarfcn = msg.sib2.freqInfo.ulCarrierFreq;
1006 m_sib2ReceivedTrace(m_imsi, m_cellId, m_rnti);
1007 NrUeCmacSapProvider::RachConfig rc;
1008 rc.numberOfRaPreambles = msg.sib2.radioResourceConfigCommon.rachConfigCommon
1009 .preambleInfo.numberOfRaPreambles;
1010 rc.preambleTransMax = msg.sib2.radioResourceConfigCommon.rachConfigCommon
1011 .raSupervisionInfo.preambleTransMax;
1012 rc.raResponseWindowSize = msg.sib2.radioResourceConfigCommon.rachConfigCommon
1013 .raSupervisionInfo.raResponseWindowSize;
1014 rc.connEstFailCount =
1015 msg.sib2.radioResourceConfigCommon.rachConfigCommon.txFailParam.connEstFailCount;
1016 m_connEstFailCountLimit = rc.connEstFailCount;
1017 NS_ASSERT_MSG(m_connEstFailCountLimit > 0 && m_connEstFailCountLimit < 5,
1018 "SIB2 msg contains wrong value " << m_connEstFailCountLimit
1019 << "of connEstFailCount");
1020 m_cmacSapProvider.at(GetPrimaryUlIndex())->ConfigureRach(rc);
1021 m_cphySapProvider.at(GetPrimaryUlIndex())->ConfigureUplink(m_ulEarfcn, m_ulBandwidth);
1022 m_cphySapProvider.at(GetPrimaryDlIndex())
1023 ->ConfigureUplink(m_ulEarfcn,
1024 m_ulBandwidth); // also needs to know that UL is configured, e.g.,
1025 // when FDD used it is necessary, otherwise CQI
1026 // will not be generated and routed to UL UE PHY
1027 m_cphySapProvider.at(GetPrimaryUlIndex())
1028 ->ConfigureReferenceSignalPower(
1029 msg.sib2.radioResourceConfigCommon.pdschConfigCommon.referenceSignalPower);
1031 {
1032 m_cphySapProvider.at(GetPrimaryUlIndex())->SetDlBandwidth(m_ulBandwidth);
1033 }
1034 if (m_state == IDLE_WAIT_SIB2)
1035 {
1036 NS_ASSERT(m_connectionPending);
1037 StartConnection();
1038 }
1039 break;
1040
1041 default: // IDLE_START, IDLE_CELL_SEARCH, IDLE_WAIT_MIB, IDLE_WAIT_MIB_SIB1, IDLE_WAIT_SIB1
1042 // do nothing
1043 break;
1044 }
1045 }
1046}
1047
1048void
1049NrUeRrc::DoRecvRrcConnectionSetup(NrRrcSap::RrcConnectionSetup msg)
1050{
1051 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
1052 switch (m_state)
1053 {
1054 case IDLE_CONNECTING: {
1055 ApplyRadioResourceConfigDedicated(msg.radioResourceConfigDedicated);
1056 m_connEstFailCount = 0;
1057 m_connectionTimeout.Cancel();
1058 SwitchToState(CONNECTED_NORMALLY);
1059 m_leaveConnectedMode = false;
1060 NrRrcSap::RrcConnectionSetupCompleted msg2;
1061 msg2.rrcTransactionIdentifier = msg.rrcTransactionIdentifier;
1062 m_rrcSapUser->SendRrcConnectionSetupCompleted(msg2);
1063 m_asSapUser->NotifyConnectionSuccessful();
1064 m_cmacSapProvider.at(GetPrimaryUlIndex())->NotifyConnectionSuccessful();
1065 m_connectionEstablishedTrace(m_imsi, m_cellId, m_rnti);
1066 NS_ABORT_MSG_IF(m_noOfSyncIndications > 0,
1067 "Sync indications should be zero "
1068 "when a new RRC connection is established. Current value = "
1069 << (uint16_t)m_noOfSyncIndications);
1070 }
1071 break;
1072
1073 default:
1074 NS_FATAL_ERROR("method unexpected in state " << ToString(m_state));
1075 break;
1076 }
1077}
1078
1079void
1080NrUeRrc::DoRecvRrcConnectionReconfiguration(NrRrcSap::RrcConnectionReconfiguration msg)
1081{
1082 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
1083 NS_LOG_INFO("DoRecvRrcConnectionReconfiguration haveNonCriticalExtension:"
1084 << msg.haveNonCriticalExtension);
1085 switch (m_state)
1086 {
1087 case CONNECTED_NORMALLY:
1088 if (msg.haveMobilityControlInfo)
1089 {
1090 NS_LOG_INFO("haveMobilityControlInfo == true");
1091 SwitchToState(CONNECTED_HANDOVER);
1092 if (m_radioLinkFailureDetected.IsPending())
1093 {
1094 ResetRlfParams();
1095 }
1096 const NrRrcSap::MobilityControlInfo& mci = msg.mobilityControlInfo;
1097 m_handoverStartTrace(m_imsi, m_cellId, m_rnti, mci.targetPhysCellId);
1098 // We should reset the MACs and PHYs for all the component carriers
1099 for (auto cmacSapProvider : m_cmacSapProvider)
1100 {
1101 cmacSapProvider->Reset();
1102 }
1103 for (auto cphySapProvider : m_cphySapProvider)
1104 {
1105 cphySapProvider->Reset();
1106 }
1107 m_ccmRrcSapProvider->Reset();
1108 StorePreviousCellId(m_cellId);
1109 m_cellId = mci.targetPhysCellId;
1110 NS_ASSERT(mci.haveCarrierFreq);
1111 NS_ASSERT(mci.haveCarrierBandwidth);
1112 m_cphySapProvider.at(GetPrimaryDlIndex())
1113 ->SynchronizeWithGnb(m_cellId, mci.carrierFreq.dlCarrierFreq);
1114 m_cphySapProvider.at(GetPrimaryDlIndex())
1115 ->SetDlBandwidth(mci.carrierBandwidth.dlBandwidth);
1117 {
1118 m_cphySapProvider.at(GetPrimaryUlIndex())
1119 ->SetDlBandwidth(mci.carrierBandwidth.ulBandwidth);
1120 }
1121 m_cphySapProvider.at(GetPrimaryUlIndex())
1122 ->ConfigureUplink(mci.carrierFreq.ulCarrierFreq, mci.carrierBandwidth.ulBandwidth);
1123 m_rnti = msg.mobilityControlInfo.newUeIdentity;
1124 m_srb0->m_rlc->SetRnti(m_rnti);
1125 NS_ASSERT_MSG(
1126 mci.haveRachConfigDedicated,
1127 "handover is only supported with non-contention-based random access procedure");
1128 m_cmacSapProvider.at(GetPrimaryUlIndex())
1129 ->StartNonContentionBasedRandomAccessProcedure(
1130 m_rnti,
1131 mci.rachConfigDedicated.raPreambleIndex,
1132 mci.rachConfigDedicated.raPrachMaskIndex);
1133 m_cphySapProvider.at(GetPrimaryUlIndex())->SetRnti(m_rnti);
1134 m_cphySapProvider.at(GetPrimaryDlIndex())->SetRnti(m_rnti);
1135 m_cmacSapProvider.at(GetPrimaryUlIndex())->SetRnti(m_rnti);
1136 m_cmacSapProvider.at(GetPrimaryDlIndex())->SetRnti(m_rnti);
1137 m_lastRrcTransactionIdentifier = msg.rrcTransactionIdentifier;
1138 NS_ASSERT(msg.haveRadioResourceConfigDedicated);
1139
1140 // we re-establish SRB1 by creating a new entity
1141 // note that we can't dispose the old entity now, because
1142 // it's in the current stack, so we would corrupt the stack
1143 // if we did so. Hence we schedule it for later disposal
1144 m_srb1Old = m_srb1;
1145 Simulator::ScheduleNow(&NrUeRrc::DisposeOldSrb1, this);
1146 m_srb1 =
1147 nullptr; // new instance will be be created within ApplyRadioResourceConfigDedicated
1148
1149 m_drbMap.clear(); // dispose all DRBs
1150 ApplyRadioResourceConfigDedicated(msg.radioResourceConfigDedicated);
1151 if (msg.haveNonCriticalExtension)
1152 {
1153 NS_LOG_DEBUG(this << "RNTI " << m_rnti
1154 << " Handover. Configuring secondary carriers");
1155 ApplyRadioResourceConfigDedicatedSecondaryCarrier(msg.nonCriticalExtension);
1156 }
1157
1158 if (msg.haveMeasConfig)
1159 {
1160 ApplyMeasConfig(msg.measConfig);
1161 }
1162 // RRC connection reconfiguration completed will be sent
1163 // after handover is complete
1164 }
1165 else
1166 {
1167 NS_LOG_INFO("haveMobilityControlInfo == false");
1168 if (msg.haveNonCriticalExtension)
1169 {
1170 ApplyRadioResourceConfigDedicatedSecondaryCarrier(msg.nonCriticalExtension);
1171 NS_LOG_DEBUG(this << "RNTI " << m_rnti << " Configured for CA");
1172 }
1173 if (msg.haveRadioResourceConfigDedicated)
1174 {
1175 ApplyRadioResourceConfigDedicated(msg.radioResourceConfigDedicated);
1176 }
1177 if (msg.haveMeasConfig)
1178 {
1179 ApplyMeasConfig(msg.measConfig);
1180 }
1181 NrRrcSap::RrcConnectionReconfigurationCompleted msg2;
1182 msg2.rrcTransactionIdentifier = msg.rrcTransactionIdentifier;
1184 m_connectionReconfigurationTrace(m_imsi, m_cellId, m_rnti);
1185 }
1186 break;
1187
1188 default:
1189 NS_FATAL_ERROR("method unexpected in state " << ToString(m_state));
1190 break;
1191 }
1192}
1193
1194void
1195NrUeRrc::DoRecvRrcConnectionReestablishment(NrRrcSap::RrcConnectionReestablishment msg)
1196{
1197 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
1198 switch (m_state)
1199 {
1200 case CONNECTED_REESTABLISHING: {
1208 }
1209 break;
1210
1211 default:
1212 NS_FATAL_ERROR("method unexpected in state " << ToString(m_state));
1213 break;
1214 }
1215}
1216
1217void
1218NrUeRrc::DoRecvRrcConnectionReestablishmentReject(NrRrcSap::RrcConnectionReestablishmentReject msg)
1219{
1220 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
1221 switch (m_state)
1222 {
1223 case CONNECTED_REESTABLISHING: {
1228 m_asSapUser->NotifyConnectionReleased(); // Inform upper layers
1229 }
1230 break;
1231
1232 default:
1233 NS_FATAL_ERROR("method unexpected in state " << ToString(m_state));
1234 break;
1235 }
1236}
1237
1238void
1239NrUeRrc::DoRecvRrcConnectionRelease(NrRrcSap::RrcConnectionRelease msg)
1240{
1241 NS_LOG_FUNCTION(this << " RNTI " << m_rnti);
1243
1244 m_lastRrcTransactionIdentifier = msg.rrcTransactionIdentifier;
1245 // release resources at UE
1246 if (!m_leaveConnectedMode)
1247 {
1248 m_leaveConnectedMode = true;
1249 SwitchToState(CONNECTED_PHY_PROBLEM);
1250 m_rrcSapUser->SendIdealUeContextRemoveRequest(m_rnti);
1251 m_asSapUser->NotifyConnectionReleased();
1252 }
1253}
1254
1255void
1256NrUeRrc::DoRecvRrcConnectionReject(NrRrcSap::RrcConnectionReject msg)
1257{
1258 NS_LOG_FUNCTION(this);
1259 m_connectionTimeout.Cancel();
1260 for (uint16_t i = 0; i < m_numberOfComponentCarriers; i++)
1261 {
1262 m_cmacSapProvider.at(i)->Reset(); // reset the MAC
1263 }
1264 m_hasReceivedSib2 = false; // invalidate the previously received SIB2
1265 SwitchToState(IDLE_CAMPED_NORMALLY);
1266 m_asSapUser->NotifyConnectionFailed(); // inform upper layer
1267}
1268
1269void
1270NrUeRrc::DoSetNumberOfComponentCarriers(uint16_t noOfComponentCarriers)
1271{
1272 NS_LOG_FUNCTION(this);
1273 m_numberOfComponentCarriers = noOfComponentCarriers;
1274}
1275
1276void
1277NrUeRrc::SynchronizeToStrongestCell()
1278{
1279 NS_LOG_FUNCTION(this);
1280 NS_ASSERT(m_state == IDLE_CELL_SEARCH);
1281
1282 uint16_t maxRsrpCellId = 0;
1283 double maxRsrp = -std::numeric_limits<double>::infinity();
1284 double minRsrp = -140.0; // Minimum RSRP in dBm a UE can report
1285
1286 for (auto it = m_storedMeasValues.begin(); it != m_storedMeasValues.end(); it++)
1287 {
1288 /*
1289 * This block attempts to find a cell with strongest RSRP and has not
1290 * yet been identified as "acceptable cell".
1291 */
1292 if (maxRsrp < it->second.rsrp && it->second.rsrp > minRsrp)
1293 {
1294 auto itCell = m_acceptableCell.find(it->first);
1295 if (itCell == m_acceptableCell.end())
1296 {
1297 maxRsrpCellId = it->first;
1298 maxRsrp = it->second.rsrp;
1299 }
1300 }
1301 }
1302
1303 if (maxRsrpCellId == 0)
1304 {
1305 NS_LOG_WARN(this << " Cell search is unable to detect surrounding cell to attach to");
1306 }
1307 else
1308 {
1309 NS_LOG_LOGIC(this << " cell " << maxRsrpCellId
1310 << " is the strongest untried surrounding cell");
1311 m_cphySapProvider.at(GetPrimaryDlIndex())->SynchronizeWithGnb(maxRsrpCellId, m_dlEarfcn);
1312 SwitchToState(IDLE_WAIT_MIB_SIB1);
1313 }
1314
1315} // end of void NrUeRrc::SynchronizeToStrongestCell ()
1316
1317void
1318NrUeRrc::EvaluateCellForSelection()
1319{
1320 NS_LOG_FUNCTION(this);
1321 NS_ASSERT(m_state == IDLE_WAIT_SIB1);
1322 NS_ASSERT(m_hasReceivedMib);
1323 NS_ASSERT(m_hasReceivedSib1);
1324 uint16_t cellId = m_lastSib1.cellAccessRelatedInfo.cellIdentity;
1325
1326 // Cell selection criteria evaluation
1327
1328 bool isSuitableCell = false;
1329 bool isAcceptableCell = false;
1330 auto storedMeasIt = m_storedMeasValues.find(cellId);
1331 double qRxLevMeas = storedMeasIt->second.rsrp;
1333 m_lastSib1.cellSelectionInfo.qRxLevMin);
1334 NS_LOG_LOGIC(this << " cell selection to cellId=" << cellId << " qrxlevmeas=" << qRxLevMeas
1335 << " dBm"
1336 << " qrxlevmin=" << qRxLevMin << " dBm");
1337
1338 if (qRxLevMeas - qRxLevMin > 0)
1339 {
1340 isAcceptableCell = true;
1341
1342 uint32_t cellCsgId = m_lastSib1.cellAccessRelatedInfo.csgIdentity;
1343 bool cellCsgIndication = m_lastSib1.cellAccessRelatedInfo.csgIndication;
1344
1345 isSuitableCell = (!cellCsgIndication || cellCsgId == m_csgWhiteList);
1346
1347 NS_LOG_LOGIC(this << " csg(ue/cell/indication)=" << m_csgWhiteList << "/" << cellCsgId
1348 << "/" << cellCsgIndication);
1349 }
1350
1351 // Cell selection decision
1352
1353 if (isSuitableCell)
1354 {
1355 m_cellId = cellId;
1356 m_cphySapProvider.at(GetPrimaryDlIndex())->SynchronizeWithGnb(cellId, m_dlEarfcn);
1357 m_cphySapProvider.at(GetPrimaryDlIndex())->SetDlBandwidth(m_dlBandwidth);
1358 m_initialCellSelectionEndOkTrace(m_imsi, cellId);
1359 // Once the UE is connected, m_connectionPending is
1360 // set to false. So, when RLF occurs and UE performs
1361 // cell selection upon leaving RRC_CONNECTED state,
1362 // the following call to DoConnect will make the
1363 // m_connectionPending to be true again. Thus,
1364 // upon calling SwitchToState (IDLE_CAMPED_NORMALLY)
1365 // UE state is instantly change to IDLE_WAIT_SIB2.
1366 // This will make the UE to read the SIB2 message
1367 // and start random access.
1368 if (!m_connectionPending)
1369 {
1370 NS_LOG_DEBUG("Calling DoConnect in state = " << ToString(m_state));
1371 DoConnect();
1372 }
1373 SwitchToState(IDLE_CAMPED_NORMALLY);
1374 }
1375 else
1376 {
1377 // ignore the MIB and SIB1 received from this cell
1378 m_hasReceivedMib = false;
1379 m_hasReceivedSib1 = false;
1380
1381 m_initialCellSelectionEndErrorTrace(m_imsi, cellId);
1382
1383 if (isAcceptableCell)
1384 {
1385 /*
1386 * The cells inserted into this list will not be considered for
1387 * subsequent cell search attempt.
1388 */
1389 m_acceptableCell.insert(cellId);
1390 }
1391
1392 SwitchToState(IDLE_CELL_SEARCH);
1393 SynchronizeToStrongestCell(); // retry to a different cell
1394 }
1395
1396} // end of void NrUeRrc::EvaluateCellForSelection ()
1397
1398void
1399NrUeRrc::ApplyRadioResourceConfigDedicatedSecondaryCarrier(
1400 NrRrcSap::NonCriticalExtensionConfiguration nonCec)
1401{
1402 NS_LOG_FUNCTION(this);
1403
1404 m_sCellToAddModList = nonCec.sCellToAddModList;
1405
1406 for (uint32_t sCellIndex : nonCec.sCellToReleaseList)
1407 {
1408 m_cphySapProvider.at(sCellIndex)->Reset();
1409 m_cmacSapProvider.at(sCellIndex)->Reset();
1410 }
1411
1412 for (auto& scell : nonCec.sCellToAddModList)
1413 {
1414 uint8_t ccId = scell.sCellIndex;
1415
1416 uint16_t physCellId = scell.cellIdentification.physCellId;
1417 uint16_t ulBand =
1418 scell.radioResourceConfigCommonSCell.ulConfiguration.ulFreqInfo.ulBandwidth;
1419 uint32_t ulEarfcn =
1420 scell.radioResourceConfigCommonSCell.ulConfiguration.ulFreqInfo.ulCarrierFreq;
1421 uint16_t dlBand = scell.radioResourceConfigCommonSCell.nonUlConfiguration.dlBandwidth;
1422 uint32_t dlEarfcn = scell.cellIdentification.dlCarrierFreq;
1423 uint8_t txMode = scell.radioResourceConfigDedicatedSCell.physicalConfigDedicatedSCell
1424 .antennaInfo.transmissionMode;
1425 uint16_t srsIndex = scell.radioResourceConfigDedicatedSCell.physicalConfigDedicatedSCell
1426 .soundingRsUlConfigDedicated.srsConfigIndex;
1427
1428 m_cphySapProvider.at(ccId)->SynchronizeWithGnb(physCellId, dlEarfcn);
1429 m_cphySapProvider.at(ccId)->SetDlBandwidth(dlBand);
1430 m_cphySapProvider.at(ccId)->ConfigureUplink(ulEarfcn, ulBand);
1431 m_cphySapProvider.at(ccId)->ConfigureReferenceSignalPower(
1432 scell.radioResourceConfigCommonSCell.nonUlConfiguration.pdschConfigCommon
1433 .referenceSignalPower);
1434 m_cphySapProvider.at(ccId)->SetTransmissionMode(txMode);
1435 m_cphySapProvider.at(ccId)->SetRnti(m_rnti);
1436 m_cmacSapProvider.at(ccId)->SetRnti(m_rnti);
1437 // update PdschConfigDedicated (i.e. P_A value)
1438 NrRrcSap::PdschConfigDedicated pdschConfigDedicated =
1439 scell.radioResourceConfigDedicatedSCell.physicalConfigDedicatedSCell
1440 .pdschConfigDedicated;
1441 double paDouble = NrRrcSap::ConvertPdschConfigDedicated2Double(pdschConfigDedicated);
1442 m_cphySapProvider.at(ccId)->SetPa(paDouble);
1443 m_cphySapProvider.at(ccId)->SetSrsConfigurationIndex(srsIndex);
1444 }
1445
1446 m_sCarrierConfiguredTrace(this, m_sCellToAddModList);
1447}
1448
1449void
1450NrUeRrc::ApplyRadioResourceConfigDedicated(NrRrcSap::RadioResourceConfigDedicated rrcd)
1451{
1452 NS_LOG_FUNCTION(this);
1453 const NrRrcSap::PhysicalConfigDedicated& pcd = rrcd.physicalConfigDedicated;
1454
1455 if (pcd.haveAntennaInfoDedicated)
1456 {
1457 m_cphySapProvider.at(GetPrimaryUlIndex())
1458 ->SetTransmissionMode(pcd.antennaInfo.transmissionMode);
1459 }
1460 if (pcd.haveSoundingRsUlConfigDedicated)
1461 {
1462 m_cphySapProvider.at(GetPrimaryUlIndex())
1463 ->SetSrsConfigurationIndex(pcd.soundingRsUlConfigDedicated.srsConfigIndex);
1464 }
1465
1466 if (pcd.havePdschConfigDedicated)
1467 {
1468 // update PdschConfigDedicated (i.e. P_A value)
1469 m_pdschConfigDedicated = pcd.pdschConfigDedicated;
1470 double paDouble = NrRrcSap::ConvertPdschConfigDedicated2Double(m_pdschConfigDedicated);
1471 m_cphySapProvider.at(GetPrimaryDlIndex())->SetPa(paDouble);
1472 }
1473
1474 auto stamIt = rrcd.srbToAddModList.begin();
1475 if (stamIt != rrcd.srbToAddModList.end())
1476 {
1477 if (!m_srb1)
1478 {
1479 // SRB1 not setup yet
1480 NS_ASSERT_MSG((m_state == IDLE_CONNECTING) || (m_state == CONNECTED_HANDOVER),
1481 "unexpected state " << ToString(m_state));
1482 NS_ASSERT_MSG(stamIt->srbIdentity == 1, "only SRB1 supported");
1483
1484 const uint8_t lcid = 1; // fixed LCID for SRB1
1485
1486 Ptr<NrRlc> rlc = CreateObject<NrRlcAm>();
1487 rlc->SetNrMacSapProvider(m_macSapProvider);
1488 rlc->SetRnti(m_rnti);
1489 rlc->SetLcId(lcid);
1490
1491 Ptr<NrPdcp> pdcp = CreateObject<NrPdcp>();
1492 pdcp->SetRnti(m_rnti);
1493 pdcp->SetLcId(lcid);
1494 pdcp->SetNrPdcpSapUser(m_drbPdcpSapUser);
1495 pdcp->SetNrRlcSapProvider(rlc->GetNrRlcSapProvider());
1496 rlc->SetNrRlcSapUser(pdcp->GetNrRlcSapUser());
1497
1498 m_srb1 = CreateObject<NrSignalingRadioBearerInfo>();
1499 m_srb1->m_rlc = rlc;
1500 m_srb1->m_pdcp = pdcp;
1501 m_srb1->m_srbIdentity = 1;
1502 m_srb1CreatedTrace(m_imsi, m_cellId, m_rnti);
1503
1504 m_srb1->m_logicalChannelConfig.priority = stamIt->logicalChannelConfig.priority;
1505 m_srb1->m_logicalChannelConfig.prioritizedBitRateKbps =
1506 stamIt->logicalChannelConfig.prioritizedBitRateKbps;
1507 m_srb1->m_logicalChannelConfig.bucketSizeDurationMs =
1508 stamIt->logicalChannelConfig.bucketSizeDurationMs;
1509 m_srb1->m_logicalChannelConfig.logicalChannelGroup =
1510 stamIt->logicalChannelConfig.logicalChannelGroup;
1511
1512 NrUeCmacSapProvider::LogicalChannelConfig lcConfig;
1513 lcConfig.priority = stamIt->logicalChannelConfig.priority;
1514 lcConfig.prioritizedBitRateKbps = stamIt->logicalChannelConfig.prioritizedBitRateKbps;
1515 lcConfig.bucketSizeDurationMs = stamIt->logicalChannelConfig.bucketSizeDurationMs;
1516 lcConfig.logicalChannelGroup = stamIt->logicalChannelConfig.logicalChannelGroup;
1517 NrMacSapUser* msu =
1518 m_ccmRrcSapProvider->ConfigureSignalBearer(lcid, lcConfig, rlc->GetNrMacSapUser());
1519 m_cmacSapProvider.at(GetPrimaryUlIndex())->AddLc(lcid, lcConfig, msu);
1520 ++stamIt;
1521 NS_ASSERT_MSG(stamIt == rrcd.srbToAddModList.end(), "at most one SrbToAdd supported");
1522
1523 NrUeRrcSapUser::SetupParameters ueParams;
1524 ueParams.srb0SapProvider = m_srb0->m_rlc->GetNrRlcSapProvider();
1525 ueParams.srb1SapProvider = m_srb1->m_pdcp->GetNrPdcpSapProvider();
1526 m_rrcSapUser->Setup(ueParams);
1527 }
1528 else
1529 {
1530 NS_LOG_INFO("request to modify SRB1 (skipping as currently not implemented)");
1531 // would need to modify m_srb1, and then propagate changes to the MAC
1532 }
1533 }
1534
1535 for (auto dtamIt = rrcd.drbToAddModList.begin(); dtamIt != rrcd.drbToAddModList.end(); ++dtamIt)
1536 {
1537 NS_LOG_INFO(this << " IMSI " << m_imsi << " adding/modifying DRBID "
1538 << (uint32_t)dtamIt->drbIdentity << " LC "
1539 << (uint32_t)dtamIt->logicalChannelIdentity);
1540 NS_ASSERT_MSG(dtamIt->logicalChannelIdentity > 2,
1541 "LCID value " << dtamIt->logicalChannelIdentity << " is reserved for SRBs");
1542
1543 auto drbMapIt = m_drbMap.find(dtamIt->drbIdentity);
1544 if (drbMapIt == m_drbMap.end())
1545 {
1546 NS_LOG_INFO("New Data Radio Bearer");
1547
1548 TypeId rlcTypeId;
1549 if (m_useRlcSm)
1550 {
1551 rlcTypeId = NrRlcSm::GetTypeId();
1552 }
1553 else
1554 {
1555 switch (dtamIt->rlcConfig.choice)
1556 {
1557 case NrRrcSap::RlcConfig::AM:
1558 rlcTypeId = NrRlcAm::GetTypeId();
1559 break;
1560
1561 case NrRrcSap::RlcConfig::UM_BI_DIRECTIONAL:
1562 rlcTypeId = NrRlcUm::GetTypeId();
1563 break;
1564
1565 default:
1566 NS_FATAL_ERROR("unsupported RLC configuration");
1567 break;
1568 }
1569 }
1570
1571 ObjectFactory rlcObjectFactory;
1572 rlcObjectFactory.SetTypeId(rlcTypeId);
1573 Ptr<NrRlc> rlc = rlcObjectFactory.Create()->GetObject<NrRlc>();
1574 rlc->SetNrMacSapProvider(m_macSapProvider);
1575 rlc->SetRnti(m_rnti);
1576 rlc->SetLcId(dtamIt->logicalChannelIdentity);
1577 if (m_useRlcSm)
1578 {
1579 // Starts the chain of calls:
1580 // DoReportBufferStatus->DoNotifyTxOpp->DoReportBufferStatus...
1581 Simulator::ScheduleNow(&NrRlcSm::Initialize, rlc);
1582 }
1583
1584 Ptr<NrDataRadioBearerInfo> drbInfo = CreateObject<NrDataRadioBearerInfo>();
1585 drbInfo->m_rlc = rlc;
1586 drbInfo->m_epsBearerIdentity = dtamIt->epsBearerIdentity;
1587 drbInfo->m_logicalChannelIdentity = dtamIt->logicalChannelIdentity;
1588 drbInfo->m_drbIdentity = dtamIt->drbIdentity;
1589
1590 // we need PDCP only for real RLC, i.e., RLC/UM or RLC/AM
1591 // if we are using RLC/SM we don't care of anything above RLC
1592 if (rlcTypeId != NrRlcSm::GetTypeId())
1593 {
1594 Ptr<NrPdcp> pdcp = CreateObject<NrPdcp>();
1595 pdcp->SetRnti(m_rnti);
1596 pdcp->SetLcId(dtamIt->logicalChannelIdentity);
1597 pdcp->SetNrPdcpSapUser(m_drbPdcpSapUser);
1598 pdcp->SetNrRlcSapProvider(rlc->GetNrRlcSapProvider());
1599 rlc->SetNrRlcSapUser(pdcp->GetNrRlcSapUser());
1600 drbInfo->m_pdcp = pdcp;
1601 }
1602
1603 m_bid2DrbidMap[dtamIt->epsBearerIdentity] = dtamIt->drbIdentity;
1604
1605 m_drbMap.insert(
1606 std::pair<uint8_t, Ptr<NrDataRadioBearerInfo>>(dtamIt->drbIdentity, drbInfo));
1607
1608 m_drbCreatedTrace(m_imsi, m_cellId, m_rnti, dtamIt->drbIdentity);
1609
1610 NrUeCmacSapProvider::LogicalChannelConfig lcConfig;
1611 lcConfig.priority = dtamIt->logicalChannelConfig.priority;
1612 lcConfig.prioritizedBitRateKbps = dtamIt->logicalChannelConfig.prioritizedBitRateKbps;
1613 lcConfig.bucketSizeDurationMs = dtamIt->logicalChannelConfig.bucketSizeDurationMs;
1614 lcConfig.logicalChannelGroup = dtamIt->logicalChannelConfig.logicalChannelGroup;
1615
1616 NS_LOG_DEBUG(this << " UE RRC RNTI " << m_rnti << " Number Of Component Carriers "
1617 << m_numberOfComponentCarriers << " lcID "
1618 << (uint16_t)dtamIt->logicalChannelIdentity);
1619 // Call AddLc of UE component carrier manager
1620 std::vector<NrUeCcmRrcSapProvider::LcsConfig> lcOnCcMapping =
1621 m_ccmRrcSapProvider->AddLc(dtamIt->logicalChannelIdentity,
1622 lcConfig,
1623 rlc->GetNrMacSapUser());
1624
1625 NS_LOG_DEBUG("Size of lcOnCcMapping vector " << lcOnCcMapping.size());
1626 auto itLcOnCcMapping = lcOnCcMapping.begin();
1627 NS_ASSERT_MSG(itLcOnCcMapping != lcOnCcMapping.end(),
1628 "Component carrier manager failed to add LC for data radio bearer");
1629
1630 for (itLcOnCcMapping = lcOnCcMapping.begin(); itLcOnCcMapping != lcOnCcMapping.end();
1631 ++itLcOnCcMapping)
1632 {
1633 NS_LOG_DEBUG("RNTI " << m_rnti << " LCG id "
1634 << (uint16_t)itLcOnCcMapping->lcConfig.logicalChannelGroup
1635 << " ComponentCarrierId "
1636 << (uint16_t)itLcOnCcMapping->componentCarrierId);
1637 uint8_t index = itLcOnCcMapping->componentCarrierId;
1638 NrUeCmacSapProvider::LogicalChannelConfig lcConfigFromCcm =
1639 itLcOnCcMapping->lcConfig;
1640 NrMacSapUser* msu = itLcOnCcMapping->msu;
1641 m_cmacSapProvider.at(index)->AddLc(dtamIt->logicalChannelIdentity,
1642 lcConfigFromCcm,
1643 msu);
1644 }
1645 }
1646 else
1647 {
1648 NS_LOG_INFO("request to modify existing DRBID");
1649 Ptr<NrDataRadioBearerInfo> drbInfo = drbMapIt->second;
1652 }
1653 }
1654
1655 for (auto dtdmIt = rrcd.drbToReleaseList.begin(); dtdmIt != rrcd.drbToReleaseList.end();
1656 ++dtdmIt)
1657 {
1658 uint8_t drbid = *dtdmIt;
1659 NS_LOG_INFO(this << " IMSI " << m_imsi << " releasing DRB " << (uint32_t)drbid);
1660 auto it = m_drbMap.find(drbid);
1661 NS_ASSERT_MSG(it != m_drbMap.end(), "could not find bearer with given lcid");
1662 m_drbMap.erase(it);
1663 m_bid2DrbidMap.erase(drbid);
1664 // Remove LCID
1665 for (uint32_t i = 0; i < m_numberOfComponentCarriers; i++)
1666 {
1667 m_cmacSapProvider.at(i)->RemoveLc(drbid + 2);
1668 }
1669 // m_ccmRrcSapProvider->RemoveLc(drbid+2);
1670 }
1671}
1672
1673void
1674NrUeRrc::ApplyMeasConfig(NrRrcSap::MeasConfig mc)
1675{
1676 NS_LOG_FUNCTION(this);
1677
1678 // perform the actions specified in 3GPP TS 36.331 section 5.5.2.1
1679
1680 // 3GPP TS 36.331 section 5.5.2.4 Measurement object removal
1681 for (auto it = mc.measObjectToRemoveList.begin(); it != mc.measObjectToRemoveList.end(); ++it)
1682 {
1683 uint8_t measObjectId = *it;
1684 NS_LOG_LOGIC(this << " deleting measObjectId " << (uint32_t)measObjectId);
1685 m_varMeasConfig.measObjectList.erase(measObjectId);
1686 auto measIdIt = m_varMeasConfig.measIdList.begin();
1687 while (measIdIt != m_varMeasConfig.measIdList.end())
1688 {
1689 if (measIdIt->second.measObjectId == measObjectId)
1690 {
1691 uint8_t measId = measIdIt->second.measId;
1692 NS_ASSERT(measId == measIdIt->first);
1693 NS_LOG_LOGIC(this << " deleting measId " << (uint32_t)measId
1694 << " because referring to measObjectId "
1695 << (uint32_t)measObjectId);
1696 // note: postfix operator preserves iterator validity
1697 m_varMeasConfig.measIdList.erase(measIdIt++);
1698 VarMeasReportListClear(measId);
1699 }
1700 else
1701 {
1702 ++measIdIt;
1703 }
1704 }
1705 }
1706
1707 // 3GPP TS 36.331 section 5.5.2.5 Measurement object addition/ modification
1708 for (auto it = mc.measObjectToAddModList.begin(); it != mc.measObjectToAddModList.end(); ++it)
1709 {
1710 // simplifying assumptions
1711 NS_ASSERT_MSG(it->measObjectEutra.cellsToRemoveList.empty(),
1712 "cellsToRemoveList not supported");
1713 NS_ASSERT_MSG(it->measObjectEutra.cellsToAddModList.empty(),
1714 "cellsToAddModList not supported");
1715 NS_ASSERT_MSG(it->measObjectEutra.cellsToRemoveList.empty(),
1716 "blackCellsToRemoveList not supported");
1717 NS_ASSERT_MSG(it->measObjectEutra.blackCellsToAddModList.empty(),
1718 "blackCellsToAddModList not supported");
1719 NS_ASSERT_MSG(it->measObjectEutra.haveCellForWhichToReportCGI == false,
1720 "cellForWhichToReportCGI is not supported");
1721
1722 uint8_t measObjectId = it->measObjectId;
1723 auto measObjectIt = m_varMeasConfig.measObjectList.find(measObjectId);
1724 if (measObjectIt != m_varMeasConfig.measObjectList.end())
1725 {
1726 NS_LOG_LOGIC("measObjectId " << (uint32_t)measObjectId << " exists, updating entry");
1727 measObjectIt->second = *it;
1728 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
1729 measIdIt != m_varMeasConfig.measIdList.end();
1730 ++measIdIt)
1731 {
1732 if (measIdIt->second.measObjectId == measObjectId)
1733 {
1734 uint8_t measId = measIdIt->second.measId;
1735 NS_LOG_LOGIC(this << " found measId " << (uint32_t)measId
1736 << " referring to measObjectId " << (uint32_t)measObjectId);
1737 VarMeasReportListClear(measId);
1738 }
1739 }
1740 }
1741 else
1742 {
1743 NS_LOG_LOGIC("measObjectId " << (uint32_t)measObjectId << " is new, adding entry");
1744 m_varMeasConfig.measObjectList[measObjectId] = *it;
1745 }
1746 }
1747
1748 // 3GPP TS 36.331 section 5.5.2.6 Reporting configuration removal
1749 for (auto it = mc.reportConfigToRemoveList.begin(); it != mc.reportConfigToRemoveList.end();
1750 ++it)
1751 {
1752 uint8_t reportConfigId = *it;
1753 NS_LOG_LOGIC(this << " deleting reportConfigId " << (uint32_t)reportConfigId);
1754 m_varMeasConfig.reportConfigList.erase(reportConfigId);
1755 auto measIdIt = m_varMeasConfig.measIdList.begin();
1756 while (measIdIt != m_varMeasConfig.measIdList.end())
1757 {
1758 if (measIdIt->second.reportConfigId == reportConfigId)
1759 {
1760 uint8_t measId = measIdIt->second.measId;
1761 NS_ASSERT(measId == measIdIt->first);
1762 NS_LOG_LOGIC(this << " deleting measId " << (uint32_t)measId
1763 << " because referring to reportConfigId "
1764 << (uint32_t)reportConfigId);
1765 // note: postfix operator preserves iterator validity
1766 m_varMeasConfig.measIdList.erase(measIdIt++);
1767 VarMeasReportListClear(measId);
1768 }
1769 else
1770 {
1771 ++measIdIt;
1772 }
1773 }
1774 }
1775
1776 // 3GPP TS 36.331 section 5.5.2.7 Reporting configuration addition/ modification
1777 for (auto it = mc.reportConfigToAddModList.begin(); it != mc.reportConfigToAddModList.end();
1778 ++it)
1779 {
1780 // simplifying assumptions
1781 NS_ASSERT_MSG(it->reportConfigEutra.triggerType == NrRrcSap::ReportConfigEutra::EVENT,
1782 "only trigger type EVENT is supported");
1783
1784 uint8_t reportConfigId = it->reportConfigId;
1785 auto reportConfigIt = m_varMeasConfig.reportConfigList.find(reportConfigId);
1786 if (reportConfigIt != m_varMeasConfig.reportConfigList.end())
1787 {
1788 NS_LOG_LOGIC("reportConfigId " << (uint32_t)reportConfigId
1789 << " exists, updating entry");
1790 m_varMeasConfig.reportConfigList[reportConfigId] = *it;
1791 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
1792 measIdIt != m_varMeasConfig.measIdList.end();
1793 ++measIdIt)
1794 {
1795 if (measIdIt->second.reportConfigId == reportConfigId)
1796 {
1797 uint8_t measId = measIdIt->second.measId;
1798 NS_LOG_LOGIC(this << " found measId " << (uint32_t)measId
1799 << " referring to reportConfigId "
1800 << (uint32_t)reportConfigId);
1801 VarMeasReportListClear(measId);
1802 }
1803 }
1804 }
1805 else
1806 {
1807 NS_LOG_LOGIC("reportConfigId " << (uint32_t)reportConfigId << " is new, adding entry");
1808 m_varMeasConfig.reportConfigList[reportConfigId] = *it;
1809 }
1810 }
1811
1812 // 3GPP TS 36.331 section 5.5.2.8 Quantity configuration
1813 if (mc.haveQuantityConfig)
1814 {
1815 NS_LOG_LOGIC(this << " setting quantityConfig");
1816 m_varMeasConfig.quantityConfig = mc.quantityConfig;
1817 // Convey the filter coefficient to PHY layer so it can configure the power control
1818 // parameter
1819 for (uint16_t i = 0; i < m_numberOfComponentCarriers; i++)
1820 {
1821 m_cphySapProvider.at(i)->SetRsrpFilterCoefficient(
1822 mc.quantityConfig.filterCoefficientRSRP);
1823 }
1824 // we calculate here the coefficient a used for Layer 3 filtering, see 3GPP TS 36.331
1825 // section 5.5.3.2
1826 m_varMeasConfig.aRsrp = std::pow(0.5, mc.quantityConfig.filterCoefficientRSRP / 4.0);
1827 m_varMeasConfig.aRsrq = std::pow(0.5, mc.quantityConfig.filterCoefficientRSRQ / 4.0);
1828 NS_LOG_LOGIC(this << " new filter coefficients: aRsrp=" << m_varMeasConfig.aRsrp
1829 << ", aRsrq=" << m_varMeasConfig.aRsrq);
1830
1831 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
1832 measIdIt != m_varMeasConfig.measIdList.end();
1833 ++measIdIt)
1834 {
1835 VarMeasReportListClear(measIdIt->second.measId);
1836 }
1837 }
1838
1839 // 3GPP TS 36.331 section 5.5.2.2 Measurement identity removal
1840 for (auto it = mc.measIdToRemoveList.begin(); it != mc.measIdToRemoveList.end(); ++it)
1841 {
1842 uint8_t measId = *it;
1843 NS_LOG_LOGIC(this << " deleting measId " << (uint32_t)measId);
1844 m_varMeasConfig.measIdList.erase(measId);
1845 VarMeasReportListClear(measId);
1846
1847 // removing time-to-trigger queues
1848 m_enteringTriggerQueue.erase(measId);
1849 m_leavingTriggerQueue.erase(measId);
1850 }
1851
1852 // 3GPP TS 36.331 section 5.5.2.3 Measurement identity addition/ modification
1853 for (auto it = mc.measIdToAddModList.begin(); it != mc.measIdToAddModList.end(); ++it)
1854 {
1855 NS_LOG_LOGIC(this << " measId " << (uint32_t)it->measId
1856 << " (measObjectId=" << (uint32_t)it->measObjectId
1857 << ", reportConfigId=" << (uint32_t)it->reportConfigId << ")");
1858 NS_ASSERT(m_varMeasConfig.measObjectList.find(it->measObjectId) !=
1859 m_varMeasConfig.measObjectList.end());
1860 NS_ASSERT(m_varMeasConfig.reportConfigList.find(it->reportConfigId) !=
1861 m_varMeasConfig.reportConfigList.end());
1862 m_varMeasConfig.measIdList[it->measId] = *it; // side effect: create new entry if not exists
1863 auto measReportIt = m_varMeasReportList.find(it->measId);
1864 if (measReportIt != m_varMeasReportList.end())
1865 {
1866 measReportIt->second.periodicReportTimer.Cancel();
1867 m_varMeasReportList.erase(measReportIt);
1868 }
1869 NS_ASSERT(m_varMeasConfig.reportConfigList.find(it->reportConfigId)
1870 ->second.reportConfigEutra.triggerType !=
1872
1873 // new empty queues for time-to-trigger
1874 std::list<PendingTrigger_t> s;
1875 m_enteringTriggerQueue[it->measId] = s;
1876 m_leavingTriggerQueue[it->measId] = s;
1877 }
1878
1879 if (mc.haveMeasGapConfig)
1880 {
1881 NS_FATAL_ERROR("measurement gaps are currently not supported");
1882 }
1883
1884 if (mc.haveSmeasure)
1885 {
1886 NS_FATAL_ERROR("s-measure is currently not supported");
1887 }
1888
1889 if (mc.haveSpeedStatePars)
1890 {
1891 NS_FATAL_ERROR("SpeedStatePars are currently not supported");
1892 }
1893}
1894
1895void
1896NrUeRrc::SaveUeMeasurements(uint16_t cellId,
1897 double rsrp,
1898 double rsrq,
1899 bool useLayer3Filtering,
1900 uint8_t componentCarrierId)
1901{
1902 NS_LOG_FUNCTION(this << cellId << +componentCarrierId << rsrp << rsrq << useLayer3Filtering);
1903
1904 auto storedMeasIt = m_storedMeasValues.find(cellId);
1905
1906 if (storedMeasIt != m_storedMeasValues.end())
1907 {
1908 if (useLayer3Filtering)
1909 {
1910 // F_n = (1-a) F_{n-1} + a M_n
1911 storedMeasIt->second.rsrp = (1 - m_varMeasConfig.aRsrp) * storedMeasIt->second.rsrp +
1912 m_varMeasConfig.aRsrp * rsrp;
1913
1914 if (std::isnan(storedMeasIt->second.rsrq))
1915 {
1916 // the previous RSRQ measurements provided UE PHY are invalid
1917 storedMeasIt->second.rsrq = rsrq; // replace it with unfiltered value
1918 }
1919 else
1920 {
1921 storedMeasIt->second.rsrq =
1922 (1 - m_varMeasConfig.aRsrq) * storedMeasIt->second.rsrq +
1923 m_varMeasConfig.aRsrq * rsrq;
1924 }
1925 }
1926 else
1927 {
1928 storedMeasIt->second.rsrp = rsrp;
1929 storedMeasIt->second.rsrq = rsrq;
1930 }
1931 }
1932 else
1933 {
1934 // first value is always unfiltered
1935 MeasValues v;
1936 v.rsrp = rsrp;
1937 v.rsrq = rsrq;
1938 v.carrierFreq = m_cphySapProvider.at(componentCarrierId)->GetDlEarfcn();
1939
1940 std::pair<uint16_t, MeasValues> val(cellId, v);
1941 auto ret = m_storedMeasValues.insert(val);
1942 NS_ASSERT_MSG(ret.second == true, "element already existed");
1943 storedMeasIt = ret.first;
1944 }
1945
1946 NS_LOG_DEBUG(this << " IMSI " << m_imsi << " state " << ToString(m_state) << ", measured cell "
1947 << cellId << ", carrier component Id " << componentCarrierId << ", new RSRP "
1948 << rsrp << " stored " << storedMeasIt->second.rsrp << ", new RSRQ " << rsrq
1949 << " stored " << storedMeasIt->second.rsrq);
1950
1951} // end of void SaveUeMeasurements
1952
1953void
1954NrUeRrc::MeasurementReportTriggering(uint8_t measId)
1955{
1956 NS_LOG_FUNCTION(this << (uint16_t)measId);
1957
1958 auto measIdIt = m_varMeasConfig.measIdList.find(measId);
1959 NS_ASSERT(measIdIt != m_varMeasConfig.measIdList.end());
1960 NS_ASSERT(measIdIt->first == measIdIt->second.measId);
1961
1962 auto reportConfigIt = m_varMeasConfig.reportConfigList.find(measIdIt->second.reportConfigId);
1963 NS_ASSERT(reportConfigIt != m_varMeasConfig.reportConfigList.end());
1964 NrRrcSap::ReportConfigEutra& reportConfigEutra = reportConfigIt->second.reportConfigEutra;
1965
1966 auto measObjectIt = m_varMeasConfig.measObjectList.find(measIdIt->second.measObjectId);
1967 NS_ASSERT(measObjectIt != m_varMeasConfig.measObjectList.end());
1968 NrRrcSap::MeasObjectEutra& measObjectEutra = measObjectIt->second.measObjectEutra;
1969
1970 auto measReportIt = m_varMeasReportList.find(measId);
1971 bool isMeasIdInReportList = (measReportIt != m_varMeasReportList.end());
1972
1973 // we don't check the purpose field, as it is only included for
1974 // triggerType == periodical, which is not supported
1975 NS_ASSERT_MSG(reportConfigEutra.triggerType == NrRrcSap::ReportConfigEutra::EVENT,
1976 "only triggerType == event is supported");
1977 // only EUTRA is supported, no need to check for it
1978
1979 NS_LOG_LOGIC(this << " considering measId " << (uint32_t)measId);
1980 bool eventEntryCondApplicable = false;
1981 bool eventLeavingCondApplicable = false;
1982 ConcernedCells_t concernedCellsEntry;
1983 ConcernedCells_t concernedCellsLeaving;
1984
1985 /*
1986 * Find which serving cell corresponds to measObjectEutra.carrierFreq
1987 * It is used, for example, by A1 event:
1988 * See TS 36.331 5.5.4.2: "for this measurement, consider the primary or
1989 * secondary cell that is configured on the frequency indicated in the
1990 * associated measObjectEUTRA to be the serving cell"
1991 */
1992 uint16_t servingCellId = 0;
1993 for (auto cphySapProvider : m_cphySapProvider)
1994 {
1995 if (cphySapProvider->GetDlEarfcn() == measObjectEutra.carrierFreq)
1996 {
1997 servingCellId = cphySapProvider->GetCellId();
1998 }
1999 }
2000
2001 if (servingCellId == 0)
2002 {
2003 return;
2004 }
2005
2006 switch (reportConfigEutra.eventId)
2007 {
2009 /*
2010 * Event A1 (Serving becomes better than threshold)
2011 * Please refer to 3GPP TS 36.331 Section 5.5.4.2
2012 */
2013
2014 double ms; // Ms, the measurement result of the serving cell
2015 double thresh; // Thresh, the threshold parameter for this event
2016 // Hys, the hysteresis parameter for this event.
2017 double hys =
2018 nr::EutranMeasurementMapping::IeValue2ActualHysteresis(reportConfigEutra.hysteresis);
2019
2020 switch (reportConfigEutra.triggerQuantity)
2021 {
2023 ms = m_storedMeasValues[servingCellId].rsrp;
2024
2025 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2027 thresh =
2028 nr::EutranMeasurementMapping::RsrpRange2Dbm(reportConfigEutra.threshold1.range);
2029 break;
2031 ms = m_storedMeasValues[servingCellId].rsrq;
2032 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2034 thresh = nr::EutranMeasurementMapping::RsrqRange2Db(reportConfigEutra.threshold1.range);
2035 break;
2036 default:
2037 NS_FATAL_ERROR("unsupported triggerQuantity");
2038 break;
2039 }
2040
2041 // Inequality A1-1 (Entering condition): Ms - Hys > Thresh
2042 bool entryCond = ms - hys > thresh;
2043
2044 if (entryCond)
2045 {
2046 if (!isMeasIdInReportList)
2047 {
2048 concernedCellsEntry.push_back(servingCellId);
2049 eventEntryCondApplicable = true;
2050 }
2051 else
2052 {
2053 /*
2054 * This is to check that the triggered cell recorded in the
2055 * VarMeasReportList is the serving cell.
2056 */
2057 NS_ASSERT(measReportIt->second.cellsTriggeredList.find(servingCellId) !=
2058 measReportIt->second.cellsTriggeredList.end());
2059 }
2060 }
2061 else if (reportConfigEutra.timeToTrigger > 0)
2062 {
2063 CancelEnteringTrigger(measId);
2064 }
2065
2066 // Inequality A1-2 (Leaving condition): Ms + Hys < Thresh
2067 bool leavingCond = ms + hys < thresh;
2068
2069 if (leavingCond)
2070 {
2071 if (isMeasIdInReportList)
2072 {
2073 /*
2074 * This is to check that the triggered cell recorded in the
2075 * VarMeasReportList is the serving cell.
2076 */
2077 NS_ASSERT(measReportIt->second.cellsTriggeredList.find(m_cellId) !=
2078 measReportIt->second.cellsTriggeredList.end());
2079 concernedCellsLeaving.push_back(m_cellId);
2080 eventLeavingCondApplicable = true;
2081 }
2082 }
2083 else if (reportConfigEutra.timeToTrigger > 0)
2084 {
2085 CancelLeavingTrigger(measId);
2086 }
2087
2088 NS_LOG_LOGIC(this << " event A1: serving cell " << servingCellId << " ms=" << ms
2089 << " thresh=" << thresh << " entryCond=" << entryCond
2090 << " leavingCond=" << leavingCond);
2091
2092 } // end of case NrRrcSap::ReportConfigEutra::EVENT_A1
2093
2094 break;
2095
2097 /*
2098 * Event A2 (Serving becomes worse than threshold)
2099 * Please refer to 3GPP TS 36.331 Section 5.5.4.3
2100 */
2101
2102 double ms; // Ms, the measurement result of the serving cell
2103 double thresh; // Thresh, the threshold parameter for this event
2104 // Hys, the hysteresis parameter for this event.
2105 double hys =
2106 nr::EutranMeasurementMapping::IeValue2ActualHysteresis(reportConfigEutra.hysteresis);
2107
2108 switch (reportConfigEutra.triggerQuantity)
2109 {
2111 ms = m_storedMeasValues[servingCellId].rsrp;
2112 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2114 thresh =
2115 nr::EutranMeasurementMapping::RsrpRange2Dbm(reportConfigEutra.threshold1.range);
2116 break;
2118 ms = m_storedMeasValues[servingCellId].rsrq;
2119 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2121 thresh = nr::EutranMeasurementMapping::RsrqRange2Db(reportConfigEutra.threshold1.range);
2122 break;
2123 default:
2124 NS_FATAL_ERROR("unsupported triggerQuantity");
2125 break;
2126 }
2127
2128 // Inequality A2-1 (Entering condition): Ms + Hys < Thresh
2129 bool entryCond = ms + hys < thresh;
2130
2131 if (entryCond)
2132 {
2133 if (!isMeasIdInReportList)
2134 {
2135 concernedCellsEntry.push_back(servingCellId);
2136 eventEntryCondApplicable = true;
2137 }
2138 else
2139 {
2140 /*
2141 * This is to check that the triggered cell recorded in the
2142 * VarMeasReportList is the serving cell.
2143 */
2144 NS_ASSERT(measReportIt->second.cellsTriggeredList.find(servingCellId) !=
2145 measReportIt->second.cellsTriggeredList.end());
2146 }
2147 }
2148 else if (reportConfigEutra.timeToTrigger > 0)
2149 {
2150 CancelEnteringTrigger(measId);
2151 }
2152
2153 // Inequality A2-2 (Leaving condition): Ms - Hys > Thresh
2154 bool leavingCond = ms - hys > thresh;
2155
2156 if (leavingCond)
2157 {
2158 if (isMeasIdInReportList)
2159 {
2160 /*
2161 * This is to check that the triggered cell recorded in the
2162 * VarMeasReportList is the serving cell.
2163 */
2164 NS_ASSERT(measReportIt->second.cellsTriggeredList.find(servingCellId) !=
2165 measReportIt->second.cellsTriggeredList.end());
2166 concernedCellsLeaving.push_back(servingCellId);
2167 eventLeavingCondApplicable = true;
2168 }
2169 }
2170 else if (reportConfigEutra.timeToTrigger > 0)
2171 {
2172 CancelLeavingTrigger(measId);
2173 }
2174
2175 NS_LOG_LOGIC(this << " event A2: serving cell " << servingCellId << " ms=" << ms
2176 << " thresh=" << thresh << " entryCond=" << entryCond
2177 << " leavingCond=" << leavingCond);
2178
2179 } // end of case NrRrcSap::ReportConfigEutra::EVENT_A2
2180
2181 break;
2182
2184 /*
2185 * Event A3 (Neighbour becomes offset better than PCell)
2186 * Please refer to 3GPP TS 36.331 Section 5.5.4.4
2187 */
2188
2189 double mn; // Mn, the measurement result of the neighbouring cell
2190 double ofn = measObjectEutra
2191 .offsetFreq; // Ofn, the frequency specific offset of the frequency of the
2192 double ocn = 0.0; // Ocn, the cell specific offset of the neighbour cell
2193 double mp; // Mp, the measurement result of the PCell
2194 double ofp = measObjectEutra
2195 .offsetFreq; // Ofp, the frequency specific offset of the primary frequency
2196 double ocp = 0.0; // Ocp, the cell specific offset of the PCell
2197 // Off, the offset parameter for this event.
2198 double off =
2199 nr::EutranMeasurementMapping::IeValue2ActualA3Offset(reportConfigEutra.a3Offset);
2200 // Hys, the hysteresis parameter for this event.
2201 double hys =
2202 nr::EutranMeasurementMapping::IeValue2ActualHysteresis(reportConfigEutra.hysteresis);
2203
2204 switch (reportConfigEutra.triggerQuantity)
2205 {
2207 mp = m_storedMeasValues[m_cellId].rsrp;
2208 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2210 break;
2212 mp = m_storedMeasValues[m_cellId].rsrq;
2213 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2215 break;
2216 default:
2217 NS_FATAL_ERROR("unsupported triggerQuantity");
2218 break;
2219 }
2220
2221 for (auto storedMeasIt = m_storedMeasValues.begin();
2222 storedMeasIt != m_storedMeasValues.end();
2223 ++storedMeasIt)
2224 {
2225 uint16_t cellId = storedMeasIt->first;
2226 if (cellId == m_cellId)
2227 {
2228 continue;
2229 }
2230
2231 // Only cell(s) on the frequency indicated in the associated measObject can trigger
2232 // event.
2233 if (m_storedMeasValues.at(cellId).carrierFreq != measObjectEutra.carrierFreq)
2234 {
2235 continue;
2236 }
2237
2238 switch (reportConfigEutra.triggerQuantity)
2239 {
2241 mn = storedMeasIt->second.rsrp;
2242 break;
2244 mn = storedMeasIt->second.rsrq;
2245 break;
2246 default:
2247 NS_FATAL_ERROR("unsupported triggerQuantity");
2248 break;
2249 }
2250
2251 bool hasTriggered =
2252 isMeasIdInReportList && (measReportIt->second.cellsTriggeredList.find(cellId) !=
2253 measReportIt->second.cellsTriggeredList.end());
2254
2255 // Inequality A3-1 (Entering condition): Mn + Ofn + Ocn - Hys > Mp + Ofp + Ocp + Off
2256 bool entryCond = mn + ofn + ocn - hys > mp + ofp + ocp + off;
2257
2258 if (entryCond)
2259 {
2260 if (!hasTriggered)
2261 {
2262 concernedCellsEntry.push_back(cellId);
2263 eventEntryCondApplicable = true;
2264 }
2265 }
2266 else if (reportConfigEutra.timeToTrigger > 0)
2267 {
2268 CancelEnteringTrigger(measId, cellId);
2269 }
2270
2271 // Inequality A3-2 (Leaving condition): Mn + Ofn + Ocn + Hys < Mp + Ofp + Ocp + Off
2272 bool leavingCond = mn + ofn + ocn + hys < mp + ofp + ocp + off;
2273
2274 if (leavingCond)
2275 {
2276 if (hasTriggered)
2277 {
2278 concernedCellsLeaving.push_back(cellId);
2279 eventLeavingCondApplicable = true;
2280 }
2281 }
2282 else if (reportConfigEutra.timeToTrigger > 0)
2283 {
2284 CancelLeavingTrigger(measId, cellId);
2285 }
2286
2287 NS_LOG_LOGIC(this << " event A3: neighbor cell " << cellId << " mn=" << mn
2288 << " mp=" << mp << " offset=" << off << " entryCond=" << entryCond
2289 << " leavingCond=" << leavingCond);
2290
2291 } // end of for (storedMeasIt)
2292
2293 } // end of case NrRrcSap::ReportConfigEutra::EVENT_A3
2294
2295 break;
2296
2298 /*
2299 * Event A4 (Neighbour becomes better than threshold)
2300 * Please refer to 3GPP TS 36.331 Section 5.5.4.5
2301 */
2302
2303 double mn; // Mn, the measurement result of the neighbouring cell
2304 double ofn = measObjectEutra
2305 .offsetFreq; // Ofn, the frequency specific offset of the frequency of the
2306 double ocn = 0.0; // Ocn, the cell specific offset of the neighbour cell
2307 double thresh; // Thresh, the threshold parameter for this event
2308 // Hys, the hysteresis parameter for this event.
2309 double hys =
2310 nr::EutranMeasurementMapping::IeValue2ActualHysteresis(reportConfigEutra.hysteresis);
2311
2312 switch (reportConfigEutra.triggerQuantity)
2313 {
2315 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2317 thresh =
2318 nr::EutranMeasurementMapping::RsrpRange2Dbm(reportConfigEutra.threshold1.range);
2319 break;
2321 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2323 thresh = nr::EutranMeasurementMapping::RsrqRange2Db(reportConfigEutra.threshold1.range);
2324 break;
2325 default:
2326 NS_FATAL_ERROR("unsupported triggerQuantity");
2327 break;
2328 }
2329
2330 for (auto storedMeasIt = m_storedMeasValues.begin();
2331 storedMeasIt != m_storedMeasValues.end();
2332 ++storedMeasIt)
2333 {
2334 uint16_t cellId = storedMeasIt->first;
2335 if (cellId == m_cellId)
2336 {
2337 continue;
2338 }
2339
2340 switch (reportConfigEutra.triggerQuantity)
2341 {
2343 mn = storedMeasIt->second.rsrp;
2344 break;
2346 mn = storedMeasIt->second.rsrq;
2347 break;
2348 default:
2349 NS_FATAL_ERROR("unsupported triggerQuantity");
2350 break;
2351 }
2352
2353 bool hasTriggered =
2354 isMeasIdInReportList && (measReportIt->second.cellsTriggeredList.find(cellId) !=
2355 measReportIt->second.cellsTriggeredList.end());
2356
2357 // Inequality A4-1 (Entering condition): Mn + Ofn + Ocn - Hys > Thresh
2358 bool entryCond = mn + ofn + ocn - hys > thresh;
2359
2360 if (entryCond)
2361 {
2362 if (!hasTriggered)
2363 {
2364 concernedCellsEntry.push_back(cellId);
2365 eventEntryCondApplicable = true;
2366 }
2367 }
2368 else if (reportConfigEutra.timeToTrigger > 0)
2369 {
2370 CancelEnteringTrigger(measId, cellId);
2371 }
2372
2373 // Inequality A4-2 (Leaving condition): Mn + Ofn + Ocn + Hys < Thresh
2374 bool leavingCond = mn + ofn + ocn + hys < thresh;
2375
2376 if (leavingCond)
2377 {
2378 if (hasTriggered)
2379 {
2380 concernedCellsLeaving.push_back(cellId);
2381 eventLeavingCondApplicable = true;
2382 }
2383 }
2384 else if (reportConfigEutra.timeToTrigger > 0)
2385 {
2386 CancelLeavingTrigger(measId, cellId);
2387 }
2388
2389 NS_LOG_LOGIC(this << " event A4: neighbor cell " << cellId << " mn=" << mn
2390 << " thresh=" << thresh << " entryCond=" << entryCond
2391 << " leavingCond=" << leavingCond);
2392
2393 } // end of for (storedMeasIt)
2394
2395 } // end of case NrRrcSap::ReportConfigEutra::EVENT_A4
2396
2397 break;
2398
2400 /*
2401 * Event A5 (PCell becomes worse than threshold1 and neighbour
2402 * becomes better than threshold2)
2403 * Please refer to 3GPP TS 36.331 Section 5.5.4.6
2404 */
2405
2406 double mp; // Mp, the measurement result of the PCell
2407 double mn; // Mn, the measurement result of the neighbouring cell
2408 double ofn = measObjectEutra
2409 .offsetFreq; // Ofn, the frequency specific offset of the frequency of the
2410 double ocn = 0.0; // Ocn, the cell specific offset of the neighbour cell
2411 double thresh1; // Thresh1, the threshold parameter for this event
2412 double thresh2; // Thresh2, the threshold parameter for this event
2413 // Hys, the hysteresis parameter for this event.
2414 double hys =
2415 nr::EutranMeasurementMapping::IeValue2ActualHysteresis(reportConfigEutra.hysteresis);
2416
2417 switch (reportConfigEutra.triggerQuantity)
2418 {
2420 mp = m_storedMeasValues[m_cellId].rsrp;
2421 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2423 NS_ASSERT(reportConfigEutra.threshold2.choice ==
2425 thresh1 =
2426 nr::EutranMeasurementMapping::RsrpRange2Dbm(reportConfigEutra.threshold1.range);
2427 thresh2 =
2428 nr::EutranMeasurementMapping::RsrpRange2Dbm(reportConfigEutra.threshold2.range);
2429 break;
2431 mp = m_storedMeasValues[m_cellId].rsrq;
2432 NS_ASSERT(reportConfigEutra.threshold1.choice ==
2434 NS_ASSERT(reportConfigEutra.threshold2.choice ==
2436 thresh1 =
2437 nr::EutranMeasurementMapping::RsrqRange2Db(reportConfigEutra.threshold1.range);
2438 thresh2 =
2439 nr::EutranMeasurementMapping::RsrqRange2Db(reportConfigEutra.threshold2.range);
2440 break;
2441 default:
2442 NS_FATAL_ERROR("unsupported triggerQuantity");
2443 break;
2444 }
2445
2446 // Inequality A5-1 (Entering condition 1): Mp + Hys < Thresh1
2447 bool entryCond = mp + hys < thresh1;
2448
2449 if (entryCond)
2450 {
2451 for (auto storedMeasIt = m_storedMeasValues.begin();
2452 storedMeasIt != m_storedMeasValues.end();
2453 ++storedMeasIt)
2454 {
2455 uint16_t cellId = storedMeasIt->first;
2456 if (cellId == m_cellId)
2457 {
2458 continue;
2459 }
2460
2461 switch (reportConfigEutra.triggerQuantity)
2462 {
2464 mn = storedMeasIt->second.rsrp;
2465 break;
2467 mn = storedMeasIt->second.rsrq;
2468 break;
2469 default:
2470 NS_FATAL_ERROR("unsupported triggerQuantity");
2471 break;
2472 }
2473
2474 bool hasTriggered =
2475 isMeasIdInReportList && (measReportIt->second.cellsTriggeredList.find(cellId) !=
2476 measReportIt->second.cellsTriggeredList.end());
2477
2478 // Inequality A5-2 (Entering condition 2): Mn + Ofn + Ocn - Hys > Thresh2
2479
2480 entryCond = mn + ofn + ocn - hys > thresh2;
2481
2482 if (entryCond)
2483 {
2484 if (!hasTriggered)
2485 {
2486 concernedCellsEntry.push_back(cellId);
2487 eventEntryCondApplicable = true;
2488 }
2489 }
2490 else if (reportConfigEutra.timeToTrigger > 0)
2491 {
2492 CancelEnteringTrigger(measId, cellId);
2493 }
2494
2495 NS_LOG_LOGIC(this << " event A5: neighbor cell " << cellId << " mn=" << mn
2496 << " mp=" << mp << " thresh2=" << thresh2
2497 << " thresh1=" << thresh1 << " entryCond=" << entryCond);
2498
2499 } // end of for (storedMeasIt)
2500
2501 } // end of if (entryCond)
2502 else
2503 {
2504 NS_LOG_LOGIC(this << " event A5: serving cell " << m_cellId << " mp=" << mp
2505 << " thresh1=" << thresh1 << " entryCond=" << entryCond);
2506
2507 if (reportConfigEutra.timeToTrigger > 0)
2508 {
2509 CancelEnteringTrigger(measId);
2510 }
2511 }
2512
2513 if (isMeasIdInReportList)
2514 {
2515 // Inequality A5-3 (Leaving condition 1): Mp - Hys > Thresh1
2516 bool leavingCond = mp - hys > thresh1;
2517
2518 if (leavingCond)
2519 {
2520 if (reportConfigEutra.timeToTrigger == 0)
2521 {
2522 // leaving condition #2 does not have to be checked
2523
2524 for (auto storedMeasIt = m_storedMeasValues.begin();
2525 storedMeasIt != m_storedMeasValues.end();
2526 ++storedMeasIt)
2527 {
2528 uint16_t cellId = storedMeasIt->first;
2529 if (cellId == m_cellId)
2530 {
2531 continue;
2532 }
2533
2534 if (measReportIt->second.cellsTriggeredList.find(cellId) !=
2535 measReportIt->second.cellsTriggeredList.end())
2536 {
2537 concernedCellsLeaving.push_back(cellId);
2538 eventLeavingCondApplicable = true;
2539 }
2540 }
2541 } // end of if (reportConfigEutra.timeToTrigger == 0)
2542 else
2543 {
2544 // leaving condition #2 has to be checked to cancel time-to-trigger
2545
2546 for (auto storedMeasIt = m_storedMeasValues.begin();
2547 storedMeasIt != m_storedMeasValues.end();
2548 ++storedMeasIt)
2549 {
2550 uint16_t cellId = storedMeasIt->first;
2551 if (cellId == m_cellId)
2552 {
2553 continue;
2554 }
2555
2556 if (measReportIt->second.cellsTriggeredList.find(cellId) !=
2557 measReportIt->second.cellsTriggeredList.end())
2558 {
2559 switch (reportConfigEutra.triggerQuantity)
2560 {
2562 mn = storedMeasIt->second.rsrp;
2563 break;
2565 mn = storedMeasIt->second.rsrq;
2566 break;
2567 default:
2568 NS_FATAL_ERROR("unsupported triggerQuantity");
2569 break;
2570 }
2571
2572 // Inequality A5-4 (Leaving condition 2): Mn + Ofn + Ocn + Hys < Thresh2
2573
2574 leavingCond = mn + ofn + ocn + hys < thresh2;
2575
2576 if (!leavingCond)
2577 {
2578 CancelLeavingTrigger(measId, cellId);
2579 }
2580
2581 /*
2582 * Whatever the result of leaving condition #2, this
2583 * cell is still "in", because leaving condition #1
2584 * is already true.
2585 */
2586 concernedCellsLeaving.push_back(cellId);
2587 eventLeavingCondApplicable = true;
2588
2589 NS_LOG_LOGIC(this << " event A5: neighbor cell " << cellId
2590 << " mn=" << mn << " mp=" << mp
2591 << " thresh2=" << thresh2 << " thresh1=" << thresh1
2592 << " leavingCond=" << leavingCond);
2593
2594 } // end of if (measReportIt->second.cellsTriggeredList.find (cellId)
2595 // != measReportIt->second.cellsTriggeredList.end ())
2596
2597 } // end of for (storedMeasIt)
2598
2599 } // end of else of if (reportConfigEutra.timeToTrigger == 0)
2600
2601 NS_LOG_LOGIC(this << " event A5: serving cell " << m_cellId << " mp=" << mp
2602 << " thresh1=" << thresh1 << " leavingCond=" << leavingCond);
2603
2604 } // end of if (leavingCond)
2605 else
2606 {
2607 if (reportConfigEutra.timeToTrigger > 0)
2608 {
2609 CancelLeavingTrigger(measId);
2610 }
2611
2612 // check leaving condition #2
2613
2614 for (auto storedMeasIt = m_storedMeasValues.begin();
2615 storedMeasIt != m_storedMeasValues.end();
2616 ++storedMeasIt)
2617 {
2618 uint16_t cellId = storedMeasIt->first;
2619 if (cellId == m_cellId)
2620 {
2621 continue;
2622 }
2623
2624 if (measReportIt->second.cellsTriggeredList.find(cellId) !=
2625 measReportIt->second.cellsTriggeredList.end())
2626 {
2627 switch (reportConfigEutra.triggerQuantity)
2628 {
2630 mn = storedMeasIt->second.rsrp;
2631 break;
2633 mn = storedMeasIt->second.rsrq;
2634 break;
2635 default:
2636 NS_FATAL_ERROR("unsupported triggerQuantity");
2637 break;
2638 }
2639
2640 // Inequality A5-4 (Leaving condition 2): Mn + Ofn + Ocn + Hys < Thresh2
2641 leavingCond = mn + ofn + ocn + hys < thresh2;
2642
2643 if (leavingCond)
2644 {
2645 concernedCellsLeaving.push_back(cellId);
2646 eventLeavingCondApplicable = true;
2647 }
2648
2649 NS_LOG_LOGIC(this << " event A5: neighbor cell " << cellId << " mn=" << mn
2650 << " mp=" << mp << " thresh2=" << thresh2 << " thresh1="
2651 << thresh1 << " leavingCond=" << leavingCond);
2652
2653 } // end of if (measReportIt->second.cellsTriggeredList.find (cellId)
2654 // != measReportIt->second.cellsTriggeredList.end ())
2655
2656 } // end of for (storedMeasIt)
2657
2658 } // end of else of if (leavingCond)
2659
2660 } // end of if (isMeasIdInReportList)
2661
2662 } // end of case NrRrcSap::ReportConfigEutra::EVENT_A5
2663
2664 break;
2665
2666 default:
2667 NS_FATAL_ERROR("unsupported eventId " << reportConfigEutra.eventId);
2668 break;
2669
2670 } // switch (event type)
2671
2672 NS_LOG_LOGIC(this << " eventEntryCondApplicable=" << eventEntryCondApplicable
2673 << " eventLeavingCondApplicable=" << eventLeavingCondApplicable);
2674
2675 if (eventEntryCondApplicable)
2676 {
2677 if (reportConfigEutra.timeToTrigger == 0)
2678 {
2679 VarMeasReportListAdd(measId, concernedCellsEntry);
2680 }
2681 else
2682 {
2683 PendingTrigger_t t;
2684 t.measId = measId;
2685 t.concernedCells = concernedCellsEntry;
2686 t.timer = Simulator::Schedule(MilliSeconds(reportConfigEutra.timeToTrigger),
2687 &NrUeRrc::VarMeasReportListAdd,
2688 this,
2689 measId,
2690 concernedCellsEntry);
2691 auto enteringTriggerIt = m_enteringTriggerQueue.find(measId);
2692 NS_ASSERT(enteringTriggerIt != m_enteringTriggerQueue.end());
2693 enteringTriggerIt->second.push_back(t);
2694 }
2695 }
2696
2697 if (eventLeavingCondApplicable)
2698 {
2699 // reportOnLeave will only be set when eventId = eventA3
2700 bool reportOnLeave = (reportConfigEutra.eventId == NrRrcSap::ReportConfigEutra::EVENT_A3) &&
2701 reportConfigEutra.reportOnLeave;
2702
2703 if (reportConfigEutra.timeToTrigger == 0)
2704 {
2705 VarMeasReportListErase(measId, concernedCellsLeaving, reportOnLeave);
2706 }
2707 else
2708 {
2709 PendingTrigger_t t;
2710 t.measId = measId;
2711 t.concernedCells = concernedCellsLeaving;
2712 t.timer = Simulator::Schedule(MilliSeconds(reportConfigEutra.timeToTrigger),
2713 &NrUeRrc::VarMeasReportListErase,
2714 this,
2715 measId,
2716 concernedCellsLeaving,
2717 reportOnLeave);
2718 auto leavingTriggerIt = m_leavingTriggerQueue.find(measId);
2719 NS_ASSERT(leavingTriggerIt != m_leavingTriggerQueue.end());
2720 leavingTriggerIt->second.push_back(t);
2721 }
2722 }
2723
2724} // end of void NrUeRrc::MeasurementReportTriggering (uint8_t measId)
2725
2726void
2727NrUeRrc::CancelEnteringTrigger(uint8_t measId)
2728{
2729 NS_LOG_FUNCTION(this << (uint16_t)measId);
2730
2731 auto it1 = m_enteringTriggerQueue.find(measId);
2732 NS_ASSERT(it1 != m_enteringTriggerQueue.end());
2733
2734 if (!it1->second.empty())
2735 {
2736 for (auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2)
2737 {
2738 NS_ASSERT(it2->measId == measId);
2739 NS_LOG_LOGIC(this << " canceling entering time-to-trigger event at "
2740 << Simulator::GetDelayLeft(it2->timer).GetSeconds());
2741 Simulator::Cancel(it2->timer);
2742 }
2743
2744 it1->second.clear();
2745 }
2746}
2747
2748void
2749NrUeRrc::CancelEnteringTrigger(uint8_t measId, uint16_t cellId)
2750{
2751 NS_LOG_FUNCTION(this << (uint16_t)measId << cellId);
2752
2753 auto it1 = m_enteringTriggerQueue.find(measId);
2754 NS_ASSERT(it1 != m_enteringTriggerQueue.end());
2755
2756 auto it2 = it1->second.begin();
2757 while (it2 != it1->second.end())
2758 {
2759 NS_ASSERT(it2->measId == measId);
2760
2761 for (auto it3 = it2->concernedCells.begin(); it3 != it2->concernedCells.end(); ++it3)
2762 {
2763 if (*it3 == cellId)
2764 {
2765 it3 = it2->concernedCells.erase(it3);
2766 }
2767 }
2768
2769 if (it2->concernedCells.empty())
2770 {
2771 NS_LOG_LOGIC(this << " canceling entering time-to-trigger event at "
2772 << Simulator::GetDelayLeft(it2->timer).GetSeconds());
2773 Simulator::Cancel(it2->timer);
2774 it2 = it1->second.erase(it2);
2775 }
2776 else
2777 {
2778 it2++;
2779 }
2780 }
2781}
2782
2783void
2784NrUeRrc::CancelLeavingTrigger(uint8_t measId)
2785{
2786 NS_LOG_FUNCTION(this << (uint16_t)measId);
2787
2788 auto it1 = m_leavingTriggerQueue.find(measId);
2789 NS_ASSERT(it1 != m_leavingTriggerQueue.end());
2790
2791 if (!it1->second.empty())
2792 {
2793 for (auto it2 = it1->second.begin(); it2 != it1->second.end(); ++it2)
2794 {
2795 NS_ASSERT(it2->measId == measId);
2796 NS_LOG_LOGIC(this << " canceling leaving time-to-trigger event at "
2797 << Simulator::GetDelayLeft(it2->timer).GetSeconds());
2798 Simulator::Cancel(it2->timer);
2799 }
2800
2801 it1->second.clear();
2802 }
2803}
2804
2805void
2806NrUeRrc::CancelLeavingTrigger(uint8_t measId, uint16_t cellId)
2807{
2808 NS_LOG_FUNCTION(this << (uint16_t)measId << cellId);
2809
2810 auto it1 = m_leavingTriggerQueue.find(measId);
2811 NS_ASSERT(it1 != m_leavingTriggerQueue.end());
2812
2813 auto it2 = it1->second.begin();
2814 while (it2 != it1->second.end())
2815 {
2816 NS_ASSERT(it2->measId == measId);
2817
2818 for (auto it3 = it2->concernedCells.begin(); it3 != it2->concernedCells.end(); ++it3)
2819 {
2820 if (*it3 == cellId)
2821 {
2822 it3 = it2->concernedCells.erase(it3);
2823 }
2824 }
2825
2826 if (it2->concernedCells.empty())
2827 {
2828 NS_LOG_LOGIC(this << " canceling leaving time-to-trigger event at "
2829 << Simulator::GetDelayLeft(it2->timer).GetSeconds());
2830 Simulator::Cancel(it2->timer);
2831 it2 = it1->second.erase(it2);
2832 }
2833 else
2834 {
2835 it2++;
2836 }
2837 }
2838}
2839
2840void
2841NrUeRrc::VarMeasReportListAdd(uint8_t measId, ConcernedCells_t enteringCells)
2842{
2843 NS_LOG_FUNCTION(this << (uint16_t)measId);
2844 NS_ASSERT(!enteringCells.empty());
2845
2846 auto measReportIt = m_varMeasReportList.find(measId);
2847
2848 if (measReportIt == m_varMeasReportList.end())
2849 {
2850 VarMeasReport r;
2851 r.measId = measId;
2852 std::pair<uint8_t, VarMeasReport> val(measId, r);
2853 auto ret = m_varMeasReportList.insert(val);
2854 NS_ASSERT_MSG(ret.second == true, "element already existed");
2855 measReportIt = ret.first;
2856 }
2857
2858 NS_ASSERT(measReportIt != m_varMeasReportList.end());
2859
2860 for (auto it = enteringCells.begin(); it != enteringCells.end(); ++it)
2861 {
2862 measReportIt->second.cellsTriggeredList.insert(*it);
2863 }
2864
2865 NS_ASSERT(!measReportIt->second.cellsTriggeredList.empty());
2866
2867 // #issue 224, schedule only when there is no periodic event scheduled already
2868 if (!measReportIt->second.periodicReportTimer.IsPending())
2869 {
2870 measReportIt->second.numberOfReportsSent = 0;
2871 measReportIt->second.periodicReportTimer =
2872 Simulator::Schedule(NR_UE_MEASUREMENT_REPORT_DELAY,
2873 &NrUeRrc::SendMeasurementReport,
2874 this,
2875 measId);
2876 }
2877
2878 auto enteringTriggerIt = m_enteringTriggerQueue.find(measId);
2879 NS_ASSERT(enteringTriggerIt != m_enteringTriggerQueue.end());
2880 if (!enteringTriggerIt->second.empty())
2881 {
2882 /*
2883 * Assumptions at this point:
2884 * - the call to this function was delayed by time-to-trigger;
2885 * - the time-to-trigger delay is fixed (not adaptive/dynamic); and
2886 * - the first element in the list is associated with this function call.
2887 */
2888 enteringTriggerIt->second.pop_front();
2889
2890 if (!enteringTriggerIt->second.empty())
2891 {
2892 /*
2893 * To prevent the same set of cells triggering again in the future,
2894 * we clean up the time-to-trigger queue. This case might occur when
2895 * time-to-trigger > 200 ms.
2896 */
2897 for (auto it = enteringCells.begin(); it != enteringCells.end(); ++it)
2898 {
2899 CancelEnteringTrigger(measId, *it);
2900 }
2901 }
2902
2903 } // end of if (!enteringTriggerIt->second.empty ())
2904
2905} // end of NrUeRrc::VarMeasReportListAdd
2906
2907void
2908NrUeRrc::VarMeasReportListErase(uint8_t measId, ConcernedCells_t leavingCells, bool reportOnLeave)
2909{
2910 NS_LOG_FUNCTION(this << (uint16_t)measId);
2911 NS_ASSERT(!leavingCells.empty());
2912
2913 auto measReportIt = m_varMeasReportList.find(measId);
2914 NS_ASSERT(measReportIt != m_varMeasReportList.end());
2915
2916 for (auto it = leavingCells.begin(); it != leavingCells.end(); ++it)
2917 {
2918 measReportIt->second.cellsTriggeredList.erase(*it);
2919 }
2920
2921 if (reportOnLeave)
2922 {
2923 // runs immediately without NR_UE_MEASUREMENT_REPORT_DELAY
2924 SendMeasurementReport(measId);
2925 }
2926
2927 if (measReportIt->second.cellsTriggeredList.empty())
2928 {
2929 measReportIt->second.periodicReportTimer.Cancel();
2930 m_varMeasReportList.erase(measReportIt);
2931 }
2932
2933 auto leavingTriggerIt = m_leavingTriggerQueue.find(measId);
2934 NS_ASSERT(leavingTriggerIt != m_leavingTriggerQueue.end());
2935 if (!leavingTriggerIt->second.empty())
2936 {
2937 /*
2938 * Assumptions at this point:
2939 * - the call to this function was delayed by time-to-trigger; and
2940 * - the time-to-trigger delay is fixed (not adaptive/dynamic); and
2941 * - the first element in the list is associated with this function call.
2942 */
2943 leavingTriggerIt->second.pop_front();
2944
2945 if (!leavingTriggerIt->second.empty())
2946 {
2947 /*
2948 * To prevent the same set of cells triggering again in the future,
2949 * we clean up the time-to-trigger queue. This case might occur when
2950 * time-to-trigger > 200 ms.
2951 */
2952 for (auto it = leavingCells.begin(); it != leavingCells.end(); ++it)
2953 {
2954 CancelLeavingTrigger(measId, *it);
2955 }
2956 }
2957
2958 } // end of if (!leavingTriggerIt->second.empty ())
2959
2960} // end of NrUeRrc::VarMeasReportListErase
2961
2962void
2963NrUeRrc::VarMeasReportListClear(uint8_t measId)
2964{
2965 NS_LOG_FUNCTION(this << (uint16_t)measId);
2966
2967 // remove the measurement reporting entry for this measId from the VarMeasReportList
2968 auto measReportIt = m_varMeasReportList.find(measId);
2969 if (measReportIt != m_varMeasReportList.end())
2970 {
2971 NS_LOG_LOGIC(this << " deleting existing report for measId " << (uint16_t)measId);
2972 measReportIt->second.periodicReportTimer.Cancel();
2973 m_varMeasReportList.erase(measReportIt);
2974 }
2975
2976 CancelEnteringTrigger(measId);
2977 CancelLeavingTrigger(measId);
2978}
2979
2980void
2981NrUeRrc::SendMeasurementReport(uint8_t measId)
2982{
2983 NS_LOG_FUNCTION(this << (uint16_t)measId);
2984 // 3GPP TS 36.331 section 5.5.5 Measurement reporting
2985
2986 auto measIdIt = m_varMeasConfig.measIdList.find(measId);
2987 NS_ASSERT(measIdIt != m_varMeasConfig.measIdList.end());
2988
2989 auto reportConfigIt = m_varMeasConfig.reportConfigList.find(measIdIt->second.reportConfigId);
2990 NS_ASSERT(reportConfigIt != m_varMeasConfig.reportConfigList.end());
2991 NrRrcSap::ReportConfigEutra& reportConfigEutra = reportConfigIt->second.reportConfigEutra;
2992
2993 NrRrcSap::MeasurementReport measurementReport;
2994 NrRrcSap::MeasResults& measResults = measurementReport.measResults;
2995 measResults.measId = measId;
2996
2997 auto measReportIt = m_varMeasReportList.find(measId);
2998 if (measReportIt == m_varMeasReportList.end())
2999 {
3000 NS_LOG_ERROR("no entry found in m_varMeasReportList for measId " << (uint32_t)measId);
3001 }
3002 else
3003 {
3004 auto servingMeasIt = m_storedMeasValues.find(m_cellId);
3005 NS_ASSERT(servingMeasIt != m_storedMeasValues.end());
3006 measResults.measResultPCell.rsrpResult =
3007 nr::EutranMeasurementMapping::Dbm2RsrpRange(servingMeasIt->second.rsrp);
3008 measResults.measResultPCell.rsrqResult =
3009 nr::EutranMeasurementMapping::Db2RsrqRange(servingMeasIt->second.rsrq);
3010 NS_LOG_INFO(this << " reporting serving cell "
3011 "RSRP "
3012 << +measResults.measResultPCell.rsrpResult << " ("
3013 << servingMeasIt->second.rsrp
3014 << " dBm) "
3015 "RSRQ "
3016 << +measResults.measResultPCell.rsrqResult << " ("
3017 << servingMeasIt->second.rsrq << " dB)");
3018
3019 measResults.haveMeasResultServFreqList = false;
3020 for (uint16_t componentCarrierId = 1; componentCarrierId < m_numberOfComponentCarriers;
3021 componentCarrierId++)
3022 {
3023 const uint16_t cellId = m_cphySapProvider.at(componentCarrierId)->GetCellId();
3024 auto measValuesIt = m_storedMeasValues.find(cellId);
3025 if (measValuesIt != m_storedMeasValues.end())
3026 {
3027 measResults.haveMeasResultServFreqList = true;
3028 NrRrcSap::MeasResultServFreq measResultServFreq;
3029 measResultServFreq.servFreqId = componentCarrierId;
3030 measResultServFreq.haveMeasResultSCell = true;
3031 measResultServFreq.measResultSCell.rsrpResult =
3032 nr::EutranMeasurementMapping::Dbm2RsrpRange(measValuesIt->second.rsrp);
3033 measResultServFreq.measResultSCell.rsrqResult =
3034 nr::EutranMeasurementMapping::Db2RsrqRange(measValuesIt->second.rsrq);
3035 measResultServFreq.haveMeasResultBestNeighCell = false;
3036 measResults.measResultServFreqList.push_back(measResultServFreq);
3037 }
3038 }
3039
3040 measResults.haveMeasResultNeighCells = false;
3041
3042 if (!(measReportIt->second.cellsTriggeredList.empty()))
3043 {
3044 std::multimap<double, uint16_t> sortedNeighCells;
3045 for (auto cellsTriggeredIt = measReportIt->second.cellsTriggeredList.begin();
3046 cellsTriggeredIt != measReportIt->second.cellsTriggeredList.end();
3047 ++cellsTriggeredIt)
3048 {
3049 uint16_t cellId = *cellsTriggeredIt;
3050 if (cellId != m_cellId)
3051 {
3052 auto neighborMeasIt = m_storedMeasValues.find(cellId);
3053 double triggerValue;
3054 switch (reportConfigEutra.triggerQuantity)
3055 {
3057 triggerValue = neighborMeasIt->second.rsrp;
3058 break;
3060 triggerValue = neighborMeasIt->second.rsrq;
3061 break;
3062 default:
3063 NS_FATAL_ERROR("unsupported triggerQuantity");
3064 break;
3065 }
3066 sortedNeighCells.insert(std::pair<double, uint16_t>(triggerValue, cellId));
3067 }
3068 }
3069
3070 std::multimap<double, uint16_t>::reverse_iterator sortedNeighCellsIt;
3071 uint32_t count;
3072 for (sortedNeighCellsIt = sortedNeighCells.rbegin(), count = 0;
3073 sortedNeighCellsIt != sortedNeighCells.rend() &&
3074 count < reportConfigEutra.maxReportCells;
3075 ++sortedNeighCellsIt, ++count)
3076 {
3077 uint16_t cellId = sortedNeighCellsIt->second;
3078 auto neighborMeasIt = m_storedMeasValues.find(cellId);
3079 NS_ASSERT(neighborMeasIt != m_storedMeasValues.end());
3080 NrRrcSap::MeasResultEutra measResultEutra;
3081 measResultEutra.physCellId = cellId;
3082 measResultEutra.haveCgiInfo = false;
3083 measResultEutra.haveRsrpResult = true;
3084 measResultEutra.rsrpResult =
3085 nr::EutranMeasurementMapping::Dbm2RsrpRange(neighborMeasIt->second.rsrp);
3086 measResultEutra.haveRsrqResult = true;
3087 measResultEutra.rsrqResult =
3088 nr::EutranMeasurementMapping::Db2RsrqRange(neighborMeasIt->second.rsrq);
3089 NS_LOG_INFO(this << " reporting neighbor cell "
3090 << (uint32_t)measResultEutra.physCellId << " RSRP "
3091 << (uint32_t)measResultEutra.rsrpResult << " ("
3092 << neighborMeasIt->second.rsrp << " dBm)"
3093 << " RSRQ " << (uint32_t)measResultEutra.rsrqResult << " ("
3094 << neighborMeasIt->second.rsrq << " dB)");
3095 measResults.measResultListEutra.push_back(measResultEutra);
3096 measResults.haveMeasResultNeighCells = true;
3097 }
3098 }
3099 else
3100 {
3101 NS_LOG_WARN(this << " cellsTriggeredList is empty");
3102 }
3103
3104 /*
3105 * The current NrRrcSap implementation is broken in that it does not
3106 * allow for infinite values of reportAmount, which is probably the most
3107 * reasonable setting. So we just always assume infinite reportAmount.
3108 */
3109 measReportIt->second.numberOfReportsSent++;
3110 measReportIt->second.periodicReportTimer.Cancel();
3111
3112 Time reportInterval;
3113 switch (reportConfigEutra.reportInterval)
3114 {
3115 case NrRrcSap::ReportConfigEutra::MS120:
3116 reportInterval = MilliSeconds(120);
3117 break;
3118 case NrRrcSap::ReportConfigEutra::MS240:
3119 reportInterval = MilliSeconds(240);
3120 break;
3121 case NrRrcSap::ReportConfigEutra::MS480:
3122 reportInterval = MilliSeconds(480);
3123 break;
3124 case NrRrcSap::ReportConfigEutra::MS640:
3125 reportInterval = MilliSeconds(640);
3126 break;
3127 case NrRrcSap::ReportConfigEutra::MS1024:
3128 reportInterval = MilliSeconds(1024);
3129 break;
3130 case NrRrcSap::ReportConfigEutra::MS2048:
3131 reportInterval = MilliSeconds(2048);
3132 break;
3133 case NrRrcSap::ReportConfigEutra::MS5120:
3134 reportInterval = MilliSeconds(5120);
3135 break;
3136 case NrRrcSap::ReportConfigEutra::MS10240:
3137 reportInterval = MilliSeconds(10240);
3138 break;
3139 case NrRrcSap::ReportConfigEutra::MIN1:
3140 reportInterval = Seconds(60);
3141 break;
3142 case NrRrcSap::ReportConfigEutra::MIN6:
3143 reportInterval = Seconds(360);
3144 break;
3145 case NrRrcSap::ReportConfigEutra::MIN12:
3146 reportInterval = Seconds(720);
3147 break;
3148 case NrRrcSap::ReportConfigEutra::MIN30:
3149 reportInterval = Seconds(1800);
3150 break;
3151 case NrRrcSap::ReportConfigEutra::MIN60:
3152 reportInterval = Seconds(3600);
3153 break;
3154 default:
3155 NS_FATAL_ERROR("Unsupported reportInterval "
3156 << (uint16_t)reportConfigEutra.reportInterval);
3157 break;
3158 }
3159
3160 // schedule the next measurement reporting
3161 measReportIt->second.periodicReportTimer =
3162 Simulator::Schedule(reportInterval, &NrUeRrc::SendMeasurementReport, this, measId);
3163
3164 // send the measurement report to eNodeB
3165 m_rrcSapUser->SendMeasurementReport(measurementReport);
3166 }
3167}
3168
3169void
3170NrUeRrc::StartConnection()
3171{
3172 NS_LOG_FUNCTION(this << m_imsi);
3173 NS_ASSERT(m_hasReceivedMib);
3174 NS_ASSERT(m_hasReceivedSib2);
3175 m_connectionPending = false; // reset the flag
3176 SwitchToState(IDLE_RANDOM_ACCESS);
3177 m_cmacSapProvider.at(GetPrimaryUlIndex())->StartContentionBasedRandomAccessProcedure();
3178}
3179
3180void
3181NrUeRrc::LeaveConnectedMode()
3182{
3183 NS_LOG_FUNCTION(this << m_imsi);
3184 m_leaveConnectedMode = true;
3185 m_storedMeasValues.clear();
3186 ResetRlfParams();
3187
3188 for (auto measIdIt = m_varMeasConfig.measIdList.begin();
3189 measIdIt != m_varMeasConfig.measIdList.end();
3190 ++measIdIt)
3191 {
3192 VarMeasReportListClear(measIdIt->second.measId);
3193 }
3194 m_varMeasConfig.measIdList.clear();
3195
3196 m_ccmRrcSapProvider->Reset();
3197
3198 for (uint32_t i = 0; i < m_numberOfComponentCarriers; i++)
3199 {
3200 m_cmacSapProvider.at(i)->Reset(); // reset the MAC
3201 }
3202
3203 m_drbMap.clear();
3204 m_bid2DrbidMap.clear();
3205 m_srb1 = nullptr;
3206 m_hasReceivedMib = false;
3207 m_hasReceivedSib1 = false;
3208 m_hasReceivedSib2 = false;
3209
3210 for (uint32_t i = 0; i < m_numberOfComponentCarriers; i++)
3211 {
3212 m_cphySapProvider.at(i)->ResetPhyAfterRlf(); // reset the PHY
3213 }
3214 SwitchToState(IDLE_START);
3215 DoStartCellSelection(m_dlEarfcn);
3216 // Save the cell id UE was attached to
3217 StorePreviousCellId(m_cellId);
3218 m_cellId = 0;
3219 m_rnti = 0;
3220 m_srb0->m_rlc->SetRnti(m_rnti);
3221}
3222
3223void
3224NrUeRrc::ConnectionTimeout()
3225{
3226 NS_LOG_FUNCTION(this << m_imsi);
3227 ++m_connEstFailCount;
3228 if (m_connEstFailCount >= m_connEstFailCountLimit)
3229 {
3230 m_connectionTimeoutTrace(m_imsi, m_cellId, m_rnti, m_connEstFailCount);
3231 SwitchToState(CONNECTED_PHY_PROBLEM);
3232 // Assumption: The gNB connection request timer would expire
3233 // before the expiration of T300 at UE. Upon which, the gNB deletes
3234 // the UE context. Therefore, here we don't need to send the UE context
3235 // deletion request to the gNB.
3236 m_asSapUser->NotifyConnectionReleased();
3237 m_connEstFailCount = 0;
3238 }
3239 else
3240 {
3241 for (uint16_t i = 0; i < m_numberOfComponentCarriers; i++)
3242 {
3243 m_cmacSapProvider.at(i)->Reset(); // reset the MAC
3244 }
3245 m_hasReceivedSib2 = false; // invalidate the previously received SIB2
3246 SwitchToState(IDLE_CAMPED_NORMALLY);
3247 m_connectionTimeoutTrace(m_imsi, m_cellId, m_rnti, m_connEstFailCount);
3248 // Following call to UE NAS will force the UE to immediately
3249 // perform the random access to the same cell again.
3250 m_asSapUser->NotifyConnectionFailed(); // inform upper layer
3251 }
3252}
3253
3254void
3255NrUeRrc::DisposeOldSrb1()
3256{
3257 NS_LOG_FUNCTION(this);
3258 m_srb1Old = nullptr;
3259}
3260
3261uint8_t
3262NrUeRrc::Bid2Drbid(uint8_t bid)
3263{
3264 auto it = m_bid2DrbidMap.find(bid);
3265 // NS_ASSERT_MSG (it != m_bid2DrbidMap.end (), "could not find BID " << bid);
3266 if (it == m_bid2DrbidMap.end())
3267 {
3268 return 0;
3269 }
3270 else
3271 {
3272 return it->second;
3273 }
3274}
3275
3276void
3277NrUeRrc::SwitchToState(State newState)
3278{
3279 NS_LOG_FUNCTION(this << ToString(newState));
3280 State oldState = m_state;
3281 m_state = newState;
3282 NS_LOG_INFO(this << " IMSI " << m_imsi << " RNTI " << m_rnti << " UeRrc " << ToString(oldState)
3283 << " --> " << ToString(newState));
3284 m_stateTransitionTrace(m_imsi, m_cellId, m_rnti, oldState, newState);
3285
3286 switch (newState)
3287 {
3288 case IDLE_START:
3289 if (m_leaveConnectedMode)
3290 {
3291 NS_LOG_INFO("Starting initial cell selection after RLF");
3292 }
3293 else
3294 {
3295 NS_FATAL_ERROR("cannot switch to an initial state");
3296 }
3297 break;
3298
3299 case IDLE_CELL_SEARCH:
3300 case IDLE_WAIT_MIB_SIB1:
3301 case IDLE_WAIT_MIB:
3302 case IDLE_WAIT_SIB1:
3303 break;
3304
3305 case IDLE_CAMPED_NORMALLY:
3306 if (m_connectionPending)
3307 {
3308 SwitchToState(IDLE_WAIT_SIB2);
3309 }
3310 break;
3311
3312 case IDLE_WAIT_SIB2:
3313 if (m_hasReceivedSib2)
3314 {
3315 NS_ASSERT(m_connectionPending);
3316 StartConnection();
3317 }
3318 break;
3319
3320 case IDLE_RANDOM_ACCESS:
3321 case IDLE_CONNECTING:
3322 case CONNECTED_NORMALLY:
3323 case CONNECTED_HANDOVER:
3324 case CONNECTED_PHY_PROBLEM:
3325 case CONNECTED_REESTABLISHING:
3326 default:
3327 break;
3328 }
3329}
3330
3331void
3332NrUeRrc::RadioLinkFailureDetected()
3333{
3334 NS_LOG_FUNCTION(this << m_imsi << m_rnti);
3335 m_radioLinkFailureTrace(m_imsi, m_cellId, m_rnti);
3336 SwitchToState(CONNECTED_PHY_PROBLEM);
3337 m_rrcSapUser->SendIdealUeContextRemoveRequest(m_rnti);
3338 m_asSapUser->NotifyConnectionReleased();
3339}
3340
3341void
3342NrUeRrc::DoNotifyInSync()
3343{
3344 NS_LOG_FUNCTION(this << m_imsi);
3345 m_noOfSyncIndications++;
3346 NS_LOG_INFO("noOfSyncIndications " << (uint16_t)m_noOfSyncIndications);
3347 m_phySyncDetectionTrace(m_imsi, m_rnti, m_cellId, "Notify in sync", m_noOfSyncIndications);
3348 if (m_noOfSyncIndications == m_n311)
3349 {
3350 ResetRlfParams();
3351 }
3352}
3353
3354void
3355NrUeRrc::DoNotifyOutOfSync()
3356{
3357 NS_LOG_FUNCTION(this << m_imsi);
3358 m_noOfSyncIndications++;
3359 NS_LOG_INFO(this << " Total Number of Sync indications from PHY "
3360 << (uint16_t)m_noOfSyncIndications << "N310 value : " << (uint16_t)m_n310);
3361 m_phySyncDetectionTrace(m_imsi, m_rnti, m_cellId, "Notify out of sync", m_noOfSyncIndications);
3362 if (m_noOfSyncIndications == m_n310)
3363 {
3364 m_radioLinkFailureDetected =
3365 Simulator::Schedule(m_t310, &NrUeRrc::RadioLinkFailureDetected, this);
3366 if (m_radioLinkFailureDetected.IsPending())
3367 {
3368 NS_LOG_INFO("t310 started");
3369 }
3370 m_cphySapProvider.at(GetPrimaryDlIndex())->StartInSyncDetection();
3371 m_noOfSyncIndications = 0;
3372 }
3373}
3374
3375void
3376NrUeRrc::DoResetSyncIndicationCounter()
3377{
3378 NS_LOG_FUNCTION(this << m_imsi);
3379
3380 NS_LOG_DEBUG("The number of sync indication received by RRC from PHY: "
3381 << (uint16_t)m_noOfSyncIndications);
3382 m_noOfSyncIndications = 0;
3383}
3384
3385void
3386NrUeRrc::ResetRlfParams()
3387{
3388 NS_LOG_FUNCTION(this << m_imsi);
3389 m_radioLinkFailureDetected.Cancel();
3390 m_noOfSyncIndications = 0;
3391 m_cphySapProvider.at(GetPrimaryDlIndex())->ResetRlfParams();
3392}
3393
3394const std::string
3396{
3397 return g_ueRrcStateName[s];
3398}
3399
3400} // namespace ns3
virtual void NotifyConnectionSuccessful()=0
Notify the NAS that RRC Connection Establishment was successful.
virtual void NotifyConnectionReleased()=0
virtual void RecvData(Ptr< Packet > packet)=0
virtual void NotifyConnectionFailed()=0
Notify the NAS that RRC Connection Establishment failed.
static TypeId GetTypeId()
Get the type ID.
Definition nr-rlc-am.cc:74
void SetNrMacSapProvider(NrMacSapProvider *s)
Definition nr-rlc.cc:156
static TypeId GetTypeId()
Get the type ID.
Definition nr-rlc.cc:184
static TypeId GetTypeId()
Get the type ID.
Definition nr-rlc-um.cc:43
static double ConvertPdschConfigDedicated2Double(PdschConfigDedicated pdschConfigDedicated)
Definition nr-rrc-sap.h:176
Service Access Point (SAP) offered by the UE component carrier manager to the UE RRC.
virtual NrMacSapUser * ConfigureSignalBearer(uint8_t lcid, NrUeCmacSapProvider::LogicalChannelConfig lcConfig, NrMacSapUser *msu)=0
Add the Signal Bearer for a specific Ue in NrUeComponenCarrierManager.
virtual std::vector< NrUeCcmRrcSapProvider::LcsConfig > AddLc(uint8_t lcId, NrUeCmacSapProvider::LogicalChannelConfig lcConfig, NrMacSapUser *msu)=0
virtual void Reset()=0
Reset LC maps.
Service Access Point (SAP) offered by the UE RRC to the UE CCM.
void SetNrUeCphySapProvider(NrUeCphySapProvider *s)
Definition nr-ue-rrc.cc:313
NrAsSapProvider * GetAsSapProvider()
Definition nr-ue-rrc.cc:410
void SetPrimaryUlIndex(uint16_t primaryIndex)
Sets the index of the UE PHY/MAC that will be used as the primary UL PHY/MAC.
Definition nr-ue-rrc.cc:519
friend class MemberNrUeRrcSapProvider< NrUeRrc >
allow MemberNrUeRrcSapProvider<NrUeRrc> class friend access
Definition nr-ue-rrc.h:76
uint16_t GetPrimaryDlIndex() const
Returns the primary index.
Definition nr-ue-rrc.cc:537
uint8_t GetDlBandwidth() const
Definition nr-ue-rrc.cc:478
void SetNrCcmRrcSapProvider(NrUeCcmRrcSapProvider *s)
Definition nr-ue-rrc.cc:390
uint32_t GetDlEarfcn() const
Definition nr-ue-rrc.cc:485
uint16_t GetPrimaryUlIndex() const
Returns the primary index.
Definition nr-ue-rrc.cc:525
uint16_t GetRnti() const
Definition nr-ue-rrc.cc:443
uint16_t GetPreviousCellId() const
Get the previous cell id.
Definition nr-ue-rrc.cc:505
void SetAsSapUser(NrAsSapUser *s)
Definition nr-ue-rrc.cc:404
void SetPrimaryDlIndex(uint16_t primaryIndex)
Sets the index of the UE PHY/MAC that will be used as the primary UL PHY/MAC.
Definition nr-ue-rrc.cc:531
~NrUeRrc() override
Definition nr-ue-rrc.cc:140
NrUeCcmRrcSapUser * GetNrCcmRrcSapUser()
Definition nr-ue-rrc.cc:397
friend class MemberNrAsSapProvider< NrUeRrc >
allow MemberNrAsSapProvider<NrUeRrc> class friend access
Definition nr-ue-rrc.h:72
void SetUseRlcSm(bool val)
Definition nr-ue-rrc.cc:512
friend class UeMemberNrUeCmacSapUser
allow UeMemberNrUeCmacSapUser class friend access
Definition nr-ue-rrc.h:66
State GetState() const
Definition nr-ue-rrc.cc:498
static TypeId GetTypeId()
Get the type ID.
Definition nr-ue-rrc.cc:168
void SetNrMacSapProvider(NrMacSapProvider *s)
Definition nr-ue-rrc.cc:383
static const std::string ToString(NrUeRrc::State s)
uint32_t GetUlEarfcn() const
Definition nr-ue-rrc.cc:491
void SetNrUeCmacSapProvider(NrUeCmacSapProvider *s)
This function is overloaded to maintain backward compatibility.
Definition nr-ue-rrc.cc:341
void SetImsi(uint64_t imsi)
Definition nr-ue-rrc.cc:416
bool IsServingCell(uint16_t cellId) const
Definition nr-ue-rrc.cc:457
NrUeCphySapUser * GetNrUeCphySapUser()
Definition nr-ue-rrc.cc:327
NrUeRrcSapProvider * GetNrUeRrcSapProvider()
Definition nr-ue-rrc.cc:376
void InitializeSap()
Initialize SAP.
Definition nr-ue-rrc.cc:575
uint8_t GetUlBandwidth() const
Definition nr-ue-rrc.cc:471
uint16_t m_numberOfComponentCarriers
Definition nr-ue-rrc.h:1365
void SetNrUeRrcSapUser(NrUeRrcSapUser *s)
Definition nr-ue-rrc.cc:369
uint64_t GetImsi() const
Definition nr-ue-rrc.cc:437
friend class MemberNrUeCcmRrcSapUser< NrUeRrc >
allow MemberNrUeCcmRrcSapUser<NrUeRrc> class friend access
Definition nr-ue-rrc.h:78
NrUeCmacSapUser * GetNrUeCmacSapUser()
This function is overloaded to maintain backward compatibility.
Definition nr-ue-rrc.cc:355
uint16_t GetCellId() const
Definition nr-ue-rrc.cc:450
friend class NrPdcpSpecificNrPdcpSapUser< NrUeRrc >
allow NrPdcpSpecificNrPdcpSapUser<NrUeRrc> class friend access
Definition nr-ue-rrc.h:70
void InitializeSrb0()
Initialize the UE side of SRB0.
Definition nr-ue-rrc.cc:543
void StorePreviousCellId(uint16_t cellId)
Store the previous cell id.
Definition nr-ue-rrc.cc:430
Part of the RRC protocol. This Service Access Point (SAP) is used to let the UE RRC receive a message...
Part of the RRC protocol. This Service Access Point (SAP) is used by the UE RRC to send messages to t...
Definition nr-rrc-sap.h:966
virtual void SendRrcConnectionReconfigurationCompleted(RrcConnectionReconfigurationCompleted msg)=0
Send an RRCConnectionReconfigurationComplete message to the serving eNodeB during an RRC connection r...
virtual void Setup(SetupParameters params)=0
Setup function.
virtual void SendRrcConnectionRequest(RrcConnectionRequest msg)=0
Send an _RRCConnectionRequest message to the serving eNodeB during an RRC connection establishment pr...
virtual void SendRrcConnectionSetupCompleted(RrcConnectionSetupCompleted msg)=0
Send an RRCConnectionSetupComplete message to the serving eNodeB during an RRC connection establishme...
virtual void SendIdealUeContextRemoveRequest(uint16_t rnti)=0
Send UE context remove request function.
virtual void SendMeasurementReport(MeasurementReport msg)=0
Send a MeasurementReport message to the serving eNodeB during a measurement reporting procedure (Sect...
static double IeValue2ActualHysteresis(uint8_t hysteresisIeValue)
Returns the actual value of a hysteresis parameter.
Definition nr-common.cc:259
static double RsrqRange2Db(uint8_t range)
Definition nr-common.cc:231
static double IeValue2ActualQRxLevMin(int8_t qRxLevMinIeValue)
Returns the actual value of an Q-RxLevMin parameter.
Definition nr-common.cc:320
static double RsrpRange2Dbm(uint8_t range)
Definition nr-common.cc:215
static double IeValue2ActualA3Offset(int8_t a3OffsetIeValue)
Returns the actual value of an a3-Offset parameter.
Definition nr-common.cc:289
static uint8_t Dbm2RsrpRange(double dbm)
Definition nr-common.cc:223
static uint8_t Db2RsrqRange(double db)
Definition nr-common.cc:239
int8_t qRxLevMin
INTEGER (-70..-22), actual value = IE value * 2 [dBm].
Definition nr-rrc-sap.h:68
@ RSRP
Reference Signal Received Power.
Definition nr-rrc-sap.h:412
@ RSRQ
Reference Signal Received Quality.
Definition nr-rrc-sap.h:413
@ PERIODICAL
periodical report
Definition nr-rrc-sap.h:365
@ EVENT_A2
Event A2: Serving becomes worse than absolute threshold.
Definition nr-rrc-sap.h:372
@ EVENT_A1
Event A1: Serving becomes better than absolute threshold.
Definition nr-rrc-sap.h:371
@ EVENT_A4
Event A4: Neighbour becomes better than absolute threshold.
Definition nr-rrc-sap.h:374
@ EVENT_A3
Event A3: Neighbour becomes amount of offset better than PCell.
Definition nr-rrc-sap.h:373
CellSelectionInfo cellSelectionInfo
cell selection info
Definition nr-rrc-sap.h:637
CellAccessRelatedInfo cellAccessRelatedInfo
cell access related info
Definition nr-rrc-sap.h:636
@ THRESHOLD_RSRQ
RSRQ is used for the threshold.
Definition nr-rrc-sap.h:352
@ THRESHOLD_RSRP
RSRP is used for the threshold.
Definition nr-rrc-sap.h:351
uint16_t bucketSizeDurationMs
bucket size duration ms
uint8_t logicalChannelGroup
logical channel group
uint16_t prioritizedBitRateKbps
prioritize bit rate Kbps
SetupParameters structure.
Definition nr-rrc-sap.h:970
NrRlcSapProvider * srb0SapProvider
SRB0 SAP provider.
Definition nr-rrc-sap.h:971
NrPdcpSapProvider * srb1SapProvider
SRB1 SAP provider.
Definition nr-rrc-sap.h:972