The basic concept of carrier aggregation and the process of establishing carrier aggregaion in NR is almost same as LTE carrier aggregation. So I would not explain much of the basic concept on carrier aggregation in this note, please refer to LTE carrier aggregation page for the concept and process of establishing CA(carrier aggregation). In this page, I will only put some examples of establishing carrier aggregation in NR.
- Before the examples : what to look for in a CA log
- Example 01 > NR SA 2CC CA : n78 (20Mhz) + n78(20 Mhz)
- Example 02 > NR SA 2CC CA : n78 (20Mhz) + n78(20 Mhz) with Measurement Report
- When CA does not work : where to look first
If you are already familiar with the concept of Carrier aggregation from LTE, the only thing I want you to refresh your memory is following sequence. This is how CA is established in protocol sequence and I will try to provide several examples of this establishment process in this page.

The three pictures down the right hand side are the part people skip, and they are the most important part. Each one is a snapshot of the radio situation and the
Top, after step (1) - only the PCC is beaming at the phone. The throughput line is flat and low : one carrier.Middle, after step (6) - both eNB carriers are now drawn beaming at the phone, because the SCC has beenconfigured . But look at the throughput line : it has still barely moved.Bottom, after step (8) - same two beams, but now the throughput curvesteps up .
That difference between the middle and the bottom picture is the whole point of the diagram. Configuring an SCell buys you nothing at all. The throughput only moves when the SCell is
Step by step
| Step | What happens | Why it is there |
|---|---|---|
(1) |
RRC Connection Reconfiguration |
The message that carries the SCell configuration. Note that it is sent on the PCC - the UE is not listening to the SCC yet, so it could not possibly arrive any other way. |
(2) |
HARQ ACK |
A physical layer acknowledgement only. It says "I received the bits", not "I applied the configuration". Confusing these two is a classic mistake when reading a log. |
(3) |
NW Reconfig / UE Reconfig |
No message is sent. Both ends apply the new configuration internally. The UE now knows the SCell's frequency, bandwidth and reference signals - but it is not yet receiving anything there. |
(4) |
SR (Scheduling Request) |
The UE has a message to send back but no uplink grant, so it must ask for one first. |
(5) |
DCI 0 |
The uplink grant that answers the SR. Steps (4) and (5) are pure plumbing and are usually left out of textbook diagrams - they are drawn here because they explain the delay between (3) and (6). |
(6) |
RRC Connection Reconfiguration Complete |
|
(7) |
MAC CE (Activation) |
Drawn as a dashed line because it is a MAC control element, not an RRC message. One byte, and it switches the SCell on. Everything before this was preparation. |
(8) |
PDSCH |
The only step where the SCC line is used. Data now arrives on both carriers at once, and the throughput curve finally steps up. Carrier aggregation is running. |
The same steps in NR terms
The diagram is drawn with LTE names because that is where most of us learned CA, and the sequence is genuinely almost unchanged in NR. Only the vocabulary moves, so here is the translation you will need to follow the logs below.
| LTE, in the diagram | NR, in the logs below | Where to find it |
|---|---|---|
PCC / SCC |
PCell / SCell |
sCellToAddModList, sCellIndex 1 |
RRC Connection Reconfiguration |
RRCReconfiguration |
Ex 01 step [1], Ex 02 step [4] |
RRC Connection Reconfiguration Complete |
RRCReconfigurationComplete |
Ex 01 step [2], Ex 02 step [5] |
MAC CE (Activation) |
SCell Activation/Deactivation MAC CE |
Ex 01 step [3], Ex 02 step [6] |
DCI 0 |
DCI format 0_0 / 0_1 |
not captured in these logs |
PDSCH on both carriers |
PDSCH on PCell and on SCell, acknowledged by one PUCCH on the PCell |
Ex 01 steps [8] to [11], Ex 02 steps [11] to [14] |
One thing the diagram does not show, and that the NR logs make very visible : the uplink. In the diagram the SCC line carries a downlink arrow at step (8) and nothing else, and that is exactly right. The SCell has no uplink of its own, so every acknowledgement for it travels back on the PCell - which is why step (8) has arrows going out on both carriers but no arrow coming back on the SCC.
Before the examples : what to look for in a CA log
Both examples on this page are real logs, and a real log is mostly noise until you know what you are looking for. So before jumping into the messages, let me put down the handful of ideas that make a CA log readable. If you keep these five points in your head, almost every line in the logs below will explain itself.
The two carriers used in both examples
Both examples use the same pair of carriers, so it is worth working out what they actually are once. For band n78 the NR-ARFCN maps to frequency as F = 3000 MHz + 0.015 MHz x (N - 600000).
PCell ssbFrequency 621216 -> 3000 + 0.015 x (621216 - 600000) =3318.24 MHz SCell ssbFrequency 627264 -> 3000 + 0.015 x (627264 - 600000) =3408.96 MHz separation =90.72 MHz SCell absoluteFrequencyPointA 626688 -> 3400.32 MHz (reference point of the SCell carrier) carrierBandwidth 51 RB @ 30 kHz SCS -> 51 x 12 x 30 kHz = 18.36 MHz ("20 MHz" channel) locationAndBandwidth 13750 -> RIV for 51 RBs starting at RB 0 (275 x (51-1) + 0)
So both carriers sit inside band n78 (3300 - 3800 MHz) but they are about 90 MHz apart, which is far more than the 18.36 MHz that either carrier occupies. In other words this is
| PCell | SCell | |
|---|---|---|
PCI |
500 |
501 |
Band / SCS |
n78 / 30 kHz |
n78 / 30 kHz |
SSB frequency |
621216 (3318.24 MHz) |
627264 (3408.96 MHz) |
Bandwidth |
51 RB = 18.36 MHz |
51 RB = 18.36 MHz |
Serving cell index |
0 (implicit) |
sCellIndex 1 |
Uplink |
PUCCH + PUSCH |
none - downlink only |
Timing advance group |
tag-Id 0 |
tag-Id 0 (same TAG) |
Scheduling |
own PDCCH |
own PDCCH (self-scheduling) |
The Amarisoft log fields you will keep seeing
The physical layer lines in these logs are terse, so here is a translation table you can refer back to. Nothing here is CA specific - it is just what the Amarisoft trace calls things.
| Field | Meaning |
|---|---|
Which search space the PDCCH was sent in, which CCE it started at, and the aggregation level. al=2 means the DCI was spread over 2 CCEs. |
|
DCI format 1_1, the UE specific downlink assignment. Format 1_0 is the fallback format and cannot do CA properly, so seeing 1_1 is a precondition for any of this to work. |
|
The frequency domain resource allocation carried in the DCI, and the resulting PRB position at the PHY. |
|
The MCS index in the DCI, and what it resolved to : mod is bits per symbol (2 = QPSK, 4 = 16QAM, 6 = 64QAM, 8 = 256QAM) and cr is the code rate. |
|
New data indicator, redundancy version and retransmission counter. ndi toggled and rv_idx=0 with retx=0 means this is a fresh transport block, not a HARQ retransmission. |
|
The 4 bit Downlink Assignment Index of DCI 1_1. The 2 MSBs are the counter DAI (which PDSCH is this in the bundle) and the 2 LSBs are the total DAI (how many PDSCHs the UE should expect to acknowledge). This is the field that keeps the HARQ codebook in sync across carriers. |
|
The DCI field points at an entry of dl-DataToUL-ACK ; k1 in the PDSCH line is the resulting offset in slots from the PDSCH to the PUCCH that carries its ACK. |
|
Transport block size in bytes. This is the size of the MAC PDU, so it is the number you use to check that a MAC PDU you decoded actually fits. |
|
The PDSCH occupies symbols 1 to 13 of the slot. This comes straight from the SLIV in pdsch-TimeDomainAllocationList, as shown in Example 01 step [1]. |
|
The HARQ ACK bits the gNB decoded from PUCCH. "1" is one ACK bit, "11" is two ACK bits - so the number of characters tells you how big the HARQ codebook was in that slot. |
Example 01 > NR SA 2CC CA : n78 (20Mhz) + n78(20 Mhz)
This example is from the log captured by Amarisoft Callbox(gNB) and UE Simulator. Overall procedure captured in this log is as follows.
| Step | Message | Layer / cell | Dir | What happens |
|---|---|---|---|---|
Establishment - configure, then activate |
||||
[1] |
RRC / PCell |
DL |
Describes the SCell, and at the same time prepares the PCell to carry the SCell's PUCCH and CSI |
|
[2] |
RRC / PCell |
UL |
Empty message. Its presence is the proof that the UE accepted the whole configuration |
|
[3] |
MAC / PCell |
DL |
One octet bitmap, 0x02, so C1 = 1 : switch on the SCell with sCellIndex 1 |
|
[4] |
PHY / PDCCH / PCell |
DL |
The assignment for step [5]. It has to be on the PCell, because the SCell is still off |
|
[5] |
PHY / PDSCH / PCell |
DL |
tb_len=84 bytes - exactly the size of the MAC PDU in step [3] |
|
[6] |
PHY / PUCCH / PCell |
UL |
The MAC CE is acknowledged. From this moment the SCell is active |
|
Traffic - what one slot looks like with two carriers running |
||||
[7] |
PHY / PDCCH / PCell |
DL |
dai=1 : counter DAI 1, total DAI 2 - "expect two ACK bits" |
|
[8] |
PHY / PDSCH / PCell |
DL |
92 bytes, 64QAM, k1=6 |
|
[9] |
PHY / PDCCH / |
DL |
Sent on the SCell itself - self-scheduling. dai=5 : counter DAI 2 of 2 |
|
[10] |
PHY / PDSCH / |
DL |
101 bytes, 256QAM, k1=6 - the same uplink slot as step [8] |
|
[11] |
PHY / PUCCH / PCell |
UL |
ack=11 - both carriers acknowledged in a single PUCCH on the PCell |
|
Read that table once more and notice what is


[1] RrcReconfiguration for Scell Addition
This is the big one, and it is the only message in the whole example that really needs studying. It is long, but it has only three jobs, and once you split it that way it becomes easy :
Job A - prepare the PCell to carry the SCell's feedback. Everything underspCellConfig is about the PCell, not the SCell. It adds a PUCCH resource and a CSI report configuration.Job B - describe the SCell. Everything undersCellToAddModList . This is essentially a compressed version of the SCell's system information plus a dedicated configuration, handed to the UE over the PCell so that the UE never has to read SIB on the SCell.Job C - carry an unrelated NAS message. The dedicatedNAS-MessageList at the bottom is just a passenger. The network had a NAS PDU to deliver and piggybacked it here. It has nothing to do with CA.
pucch-Config -> resourceToAddModList ->pucch-ResourceId 18 startingPRB 49, secondHopPRB 1, intraSlotFrequencyHopping enabled format2 : nrofPRBs 1, nrofSymbols 2, startingSymbolIndex 2 csi-MeasConfig -> csi-ReportConfigToAddModList -> reportConfigId 1carrier 1 <-- this report is about serving cell #1 = the SCell resourcesForChannelMeasurement 0, csi-IM-ResourcesForInterference 1 reportConfigType periodic : reportSlotConfigslots80: 18 pucch-CSI-ResourceList : uplinkBandwidthPartId 0,pucch-Resource 18 reportQuantity cri-RI-PMI-CQI, cqi-Table table2 (256QAM table)
The single most important line in this whole message is
Note also that PUCCH resource 18 is a
You do not have to take that on trust - resource 18 is visible in the second screenshot of Example 02, and every field of the configuration above can be read straight off it :
configured in RRC seen in the PHY log --------------------------------------- ------------------------------------------ format2format=2 startingPRB 49prb=49 secondHopPRB 1 (freq hopping enabled)prb2=1 startingSymbolIndex 2, nrofSymbols 2symb=2:2 reportQuantity cri-RI-PMI-CQIcsi=1011 <-- the report itself and the two reports in that screenshot are at SFN816 .9 and SFN820 .9 4 radio frames apart =40 ms = slots80 at 30 kHz SCS (80 x 0.5 ms)
So the periodic CSI report for the SCell really does come back on the PCell's uplink, on a format 2 resource, exactly 40 ms apart. That single line in the log is the whole of point (2) at the top of this page, made visible.
physCellId501 absoluteFrequencySSB 627264 -> 3408.96 MHz absoluteFrequencyPointA 626688 -> 3400.32 MHz carrierBandwidth 51 RB @ kHz30 -> 18.36 MHz ssb-PositionsInBurst mediumBitmap '80'H -> 1000 0000 :only SSB index 0 is transmitted ssb-periodicityServingCell ms20 ss-PBCH-BlockPower -28 tdd-UL-DL-ConfigurationCommon : periodicity ms5, 7 DL slots + 6 DL symbols | 4 UL symbols + 2 UL slots -> at 30 kHz, 5 ms = 10 slots :D D D D D D D S U U -> the special slot S has 6 DL symbols, 4 guard symbols, 4 UL symbols pdsch-TimeDomainAllocationList : startSymbolAndLength40 -> SLIV 40 -> S=1, L=13 ->symbols 1..13 startSymbolAndLength 57 -> SLIV 57 -> S=1, L=5 -> symbols 1..5
That SLIV is worth pausing on, because it is a rare case where you can check a configuration against a physical layer log with no ambiguity. SLIV 40 decodes to start symbol 1, length 13. Now look ahead at steps [5], [8] and [10] : every PDSCH line in this log says
Then comes sCellConfigDedicated, the part that makes the SCell actually usable :
pdcch-Config -> searchSpacesToAddModList ->searchSpaceId 2 controlResourceSetId 1, monitoringSlotPeriodicityAndOffset sl1 (every slot) searchSpaceTypeue-Specific : dci-Formats formats0-1-And-1-1 <-- the SCell has its own PDCCH : self-scheduling pdsch-Config -> tci-StatesToAddModList -> tci-StateId 0, qcl ssb:0, qcl-Type typeD mcs-Tableqam256 , resourceAllocation type1, prb-BundlingType wideband pdsch-ServingCellConfig -> nrofHARQ-ProcessesForPDSCHn16 firstActiveDownlinkBWP-Id 0 tag-Id 0 <-- same timing advance group as the PCell
The search space entry tells you the scheduling mode. The SCell has its own UE specific search space monitored in every slot, so the gNB will send the SCell's DCI
Also notice what is
(a) NZP-CSI-RS for channel measurement nzp-CSI-RS-ResourceId 0 : row2 '800'H, 1 port, symbol 4, density one periodicityAndOffset slots80: 1 (= 40 ms) -> grouped into nzp-CSI-RS-ResourceSetId 0(b) CSI-IM for interference measurement csi-IM-ResourceId 0 : pattern1, subcarrier s8, symbol 8 periodicityAndOffset slots80: 1 (= same 40 ms, same offset)(c) ZP-CSI-RS for rate matching zp-CSI-RS-ResourceId 0 : row4 '100'B, 4 ports, symbol 8 periodicityAndOffset slots80: 1(d) TRS - the tracking reference signal nzp-CSI-RS-ResourceId 1 : symbol 4, slots40: 11 nzp-CSI-RS-ResourceId 2 : symbol 8, slots40: 11 nzp-CSI-RS-ResourceId 3 : symbol 4, slots40: 12 nzp-CSI-RS-ResourceId 4 : symbol 8, slots40: 12 -> grouped into nzp-CSI-RS-ResourceSetId 1 withtrs-Info true
Read them as a chain. (a) is the signal the UE measures to work out how good the SCell's channel is. (b) is a set of resource elements where the gNB deliberately sends
(d) is a different animal. Four resources in two consecutive slots (11 and 12 of every 40 slots), two symbols apart in each slot, one port, density three, tagged
{
message c1: rrcReconfiguration: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfiguration: {
nonCriticalExtension {
masterCellGroup {
cellGroupId 0,
mac-CellGroupConfig {
phr-Config setup: {
phr-PeriodicTimer sf500,
phr-ProhibitTimer sf200,
phr-Tx-PowerFactorChange dB3,
multiplePHR FALSE,
dummy FALSE,
phr-Type2OtherCell FALSE,
phr-ModeOtherCG real
},
skipUplinkTxDynamic FALSE
},
spCellConfig {
spCellConfigDedicated {
initialDownlinkBWP {
pdsch-Config setup: {
resourceAllocation resourceAllocationType1,
rbg-Size config1,
mcs-Table qam256,
prb-BundlingType staticBundling: {
bundleSize wideband
}
}
},
uplinkConfig {
initialUplinkBWP {
pucch-Config setup: {
resourceToAddModList {
{
pucch-ResourceId 18,
startingPRB 49,
intraSlotFrequencyHopping enabled,
secondHopPRB 1,
format format2: {
nrofPRBs 1,
nrofSymbols 2,
startingSymbolIndex 2
}
}
}
},
pusch-Config setup: {
txConfig codebook,
resourceAllocation resourceAllocationType1,
mcs-Table qam256,
mcs-TableTransformPrecoder qam256,
codebookSubset nonCoherent,
maxRank 1
}
}
},
csi-MeasConfig setup: {
csi-ReportConfigToAddModList {
{
reportConfigId 1,
carrier 1,
resourcesForChannelMeasurement 0,
csi-IM-ResourcesForInterference 1,
reportConfigType periodic: {
reportSlotConfig slots80: 18,
pucch-CSI-ResourceList {
{
uplinkBandwidthPartId 0,
pucch-Resource 18
}
}
},
reportQuantity cri-RI-PMI-CQI: NULL,
reportFreqConfiguration {
cqi-FormatIndicator widebandCQI
},
timeRestrictionForChannelMeasurements notConfigured,
timeRestrictionForInterferenceMeasurements notConfigured,
groupBasedBeamReporting disabled: {
},
cqi-Table table2,
subbandSize value1
}
}
},
tag-Id 0
}
},
sCellToAddModList {
{
sCellIndex 1,
sCellConfigCommon {
physCellId 501,
downlinkConfigCommon {
frequencyInfoDL {
absoluteFrequencySSB 627264,
frequencyBandList {
78
},
absoluteFrequencyPointA 626688,
scs-SpecificCarrierList {
{
offsetToCarrier 0,
subcarrierSpacing kHz30,
carrierBandwidth 51
}
}
},
initialDownlinkBWP {
genericParameters {
locationAndBandwidth 13750,
subcarrierSpacing kHz30
},
pdcch-ConfigCommon setup: {
controlResourceSetZero 10,
commonControlResourceSet {
controlResourceSetId 1,
frequencyDomainResources '011111110000000000000000000000000000000000000'B,
duration 1,
cce-REG-MappingType nonInterleaved: NULL,
precoderGranularity sameAsREG-bundle
},
searchSpaceZero 0,
commonSearchSpaceList {
{
searchSpaceId 1,
controlResourceSetId 0,
monitoringSlotPeriodicityAndOffset sl1: NULL,
monitoringSymbolsWithinSlot '10000000000000'B,
nrofCandidates {
aggregationLevel1 n0,
aggregationLevel2 n0,
aggregationLevel4 n4,
aggregationLevel8 n0,
aggregationLevel16 n0
},
searchSpaceType common: {
dci-Format0-0-AndFormat1-0 {
}
}
}
},
searchSpaceSIB1 0,
searchSpaceOtherSystemInformation 1,
pagingSearchSpace 1,
ra-SearchSpace 1
},
pdsch-ConfigCommon setup: {
pdsch-TimeDomainAllocationList {
{
mappingType typeA,
startSymbolAndLength 40
},
{
mappingType typeA,
startSymbolAndLength 57
}
}
}
}
},
ssb-PositionsInBurst mediumBitmap: '80'H,
ssb-periodicityServingCell ms20,
dmrs-TypeA-Position pos2,
ssbSubcarrierSpacing kHz30,
tdd-UL-DL-ConfigurationCommon {
referenceSubcarrierSpacing kHz30,
pattern1 {
dl-UL-TransmissionPeriodicity ms5,
nrofDownlinkSlots 7,
nrofDownlinkSymbols 6,
nrofUplinkSlots 2,
nrofUplinkSymbols 4
}
},
ss-PBCH-BlockPower -28
},
sCellConfigDedicated {
initialDownlinkBWP {
pdcch-Config setup: {
searchSpacesToAddModList {
{
searchSpaceId 2,
controlResourceSetId 1,
monitoringSlotPeriodicityAndOffset sl1: NULL,
monitoringSymbolsWithinSlot '10000000000000'B,
nrofCandidates {
aggregationLevel1 n0,
aggregationLevel2 n2,
aggregationLevel4 n1,
aggregationLevel8 n0,
aggregationLevel16 n0
},
searchSpaceType ue-Specific: {
dci-Formats formats0-1-And-1-1
}
}
}
},
pdsch-Config setup: {
dmrs-DownlinkForPDSCH-MappingTypeA setup: {
dmrs-AdditionalPosition pos1
},
tci-StatesToAddModList {
{
tci-StateId 0,
qcl-Type1 {
referenceSignal ssb: 0,
qcl-Type typeD
}
}
},
resourceAllocation resourceAllocationType1,
rbg-Size config1,
mcs-Table qam256,
prb-BundlingType staticBundling: {
bundleSize wideband
},
zp-CSI-RS-ResourceToAddModList {
{
zp-CSI-RS-ResourceId 0,
resourceMapping {
frequencyDomainAllocation row4: '100'B,
nrofPorts p4,
firstOFDMSymbolInTimeDomain 8,
cdm-Type fd-CDM2,
density one: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
periodicityAndOffset slots80: 1
}
},
p-ZP-CSI-RS-ResourceSet setup: {
zp-CSI-RS-ResourceSetId 0,
zp-CSI-RS-ResourceIdList {
0
}
}
}
},
firstActiveDownlinkBWP-Id 0,
pdcch-ServingCellConfig setup: {
},
pdsch-ServingCellConfig setup: {
nrofHARQ-ProcessesForPDSCH n16
},
csi-MeasConfig setup: {
nzp-CSI-RS-ResourceToAddModList {
{
nzp-CSI-RS-ResourceId 0,
resourceMapping {
frequencyDomainAllocation row2: '800'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density one: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots80: 1,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 1,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 11,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 2,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 8,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 11,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 3,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 12,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 4,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 8,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 12,
qcl-InfoPeriodicCSI-RS 0
}
},
nzp-CSI-RS-ResourceSetToAddModList {
{
nzp-CSI-ResourceSetId 0,
nzp-CSI-RS-Resources {
0
}
},
{
nzp-CSI-ResourceSetId 1,
nzp-CSI-RS-Resources {
1,
2,
3,
4
},
trs-Info true
}
},
csi-IM-ResourceToAddModList {
{
csi-IM-ResourceId 0,
csi-IM-ResourceElementPattern pattern1: {
subcarrierLocation-p1 s8,
symbolLocation-p1 8
},
freqBand {
startingRB 0,
nrofRBs 52
},
periodicityAndOffset slots80: 1
}
},
csi-IM-ResourceSetToAddModList {
{
csi-IM-ResourceSetId 0,
csi-IM-Resources {
0
}
}
},
csi-ResourceConfigToAddModList {
{
csi-ResourceConfigId 0,
csi-RS-ResourceSetList nzp-CSI-RS-SSB: {
nzp-CSI-RS-ResourceSetList {
0
}
},
bwp-Id 0,
resourceType periodic
},
{
csi-ResourceConfigId 1,
csi-RS-ResourceSetList csi-IM-ResourceSetList: {
0
},
bwp-Id 0,
resourceType periodic
},
{
csi-ResourceConfigId 2,
csi-RS-ResourceSetList nzp-CSI-RS-SSB: {
nzp-CSI-RS-ResourceSetList {
1
}
},
bwp-Id 0,
resourceType periodic
}
}
},
tag-Id 0
}
}
}
},
dedicatedNAS-MessageList {
'7E026FF0BE84017E0042010977000BF200F....'H
}
}
}
}
}
[2] RrcReconfigurationComplete
Four lines, no content, and that is the point. RRCReconfigurationComplete never echoes back what was configured. It only says "transaction 0 applied successfully". If the UE could not apply something in the reconfiguration - an unsupported band combination, a configuration it considers invalid - it would not send a failed version of this message. It would declare a reconfiguration failure and fall back, which in SA means going to RRC_IDLE and starting over. So in practice this empty message is a pass/fail bit : if you see it, the UE has accepted the entire SCell configuration.
Match the
{
message c1: rrcReconfigurationComplete: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfigurationComplete: {
}
}
}
[3] MAC CE to activate the Scell
At this moment the UE knows everything about the SCell but is not receiving anything on it. This one line changes that. It is the whole MAC PDU, and it contains three subPDUs :
Message:Scell AD:02 LCID:1 len=3 PAD: len=76 Scell AD:02 SCell Activation/Deactivation MAC CE, payload = 0x02LCID:1 3 bytes of SRB1 data travelling in the same PDU (this is the RRC message of a different procedure - MAC happily multiplexes them)PAD: len=76 76 bytes of padding, because the transport block was bigger than needed
0x02 = 0 0 0 0 0 1 0
| | | | | | | |
| | | | | | | +-- R = 0 (reserved)
| | | | | | +---- C1 = 1 -> activate the SCell with sCellIndex 1
| | | | | +------ C2 = 0
| | | | +-------- C3 = 0
| | | +---------- C4 = 0
| | +------------ C5 = 0
| +-------------- C6 = 0
+---------------- C7 = 0
C1 = 1 refers to
Message: Scell AD:02 LCID:1 len=3 PAD: len=76
[4] DCI to schedule PDSCH carrying the MAC CE
A MAC CE is not magic - it travels in a normal transport block on a normal PDSCH, which needs a normal DCI. This DCI is sent on the
The fields worth reading here :
ss_id=2, cce_index=0, al=2 - a UE specific search space, aggregation level 2. Normal.mcs1=17, ndi1=1, rv_idx1=0, harq_process=0 - a fresh transport block on HARQ process 0.dai=0 - binary 0000, so counter DAI = 00 and total DAI = 00. Both mean "the first, and there is one in total". Keep this next to the dai values in steps [7] and [9] and the DAI mechanism will become obvious.harq_feedback_timing=5 - the index into dl-DataToUL-ACK. Step [5] shows what it resolved to.
Message: ss_id=2 cce_index=0 al=2 dci=1_1
Data:
rb_alloc=0x32
time_domain_rsc=0
mcs1=17
ndi1=1
rv_idx1=0
harq_process=0
dai=0
tpc_command=1
pucch_rsc=0
harq_feedback_timing=5
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[5] PDSCH carrying the MAC CE
This is the transport block scheduled by step [4], and it contains the MAC PDU from step [3]. The reason to look at it is that you can verify that claim arithmetically. The log says
SCell Act/Deact MAC CE subheader 1 byte + payload 1 byte = 2 bytes
LCID 1 subPDU subheader 2 bytes + payload 3 bytes = 5 bytes
Padding subheader 1 byte + 76 bytes = 77 bytes
--------
84 bytes = tb_len
It matches exactly. This is the kind of check that turns a log from a wall of text into something you can trust : the MAC line and the PHY line are describing the same object from two different layers.
The other field to notice is
Finally,
Message: harq=0 prb=50 symb=1:13 k1=12 CW0: tb_len=84 mod=6 rv_idx=0 cr=0.80 retx=0
[6] PUCCH (HARQ ACK) for PDSCH at step [5]
The UE acknowledges the transport block, and with that the SCell is on. Reading the line :
format=1 - PUCCH format 1, the long format for 1 or 2 HARQ bits. Note that this isnot pucch-Resource 18 from step [1] ; resource 18 is a format 2 resource reserved for the SCell's CSI report. HARQ feedback and CSI reporting use different PUCCH resources, and mixing them up is a common source of confusion when reading a CA log.prb=50, prb2=0, symb=0:14 - 14 symbols long, hopping from PRB 50 to PRB 0. Long format plus frequency hopping is the standard choice at cell edge, where the uplink is the weak direction.snr=37.4, epre=-88.0 - the received quality at the gNB. 37 dB SNR is a cabled or very short range lab setup ; over the air you would rarely see above the mid twenties.
from the screenshot, all within SFN 3.175 :
slot 6 PDCCH ss_id=2 cce_index=0 al=2 dci=1_1 (step [4])
slot 6 PDSCH harq=0 prb=50 symb=1:13 k1=12 tb_len=84 (step [5], the MAC CE)
6 + 12 = 18
slot 7 PDCCH ss_id=2 cce_index=6 al=2 dci=1_1
slot 7 PDSCH harq=1 prb=43:8 symb=1:5 k1=11 tb_len=253 (a second RRCReconfiguration)
7 + 11 = 18
slot 18 PUCCH format=1 ... ack=11 (step [6])
Both PDSCHs were deliberately aimed at the
Message: format=1 prb=50 prb2=0 symb=0:14 cs=0 occ=0 ack=11 snr=37.4 epre=-88.0

Everything from here on is no longer establishment. The SCell is active, and steps [7] to [11] are simply what a single downlink slot looks like when two carriers are running. Read them as one group of five rather than five separate events, because that is what they are :
[7] DCI (PDSCH) scheduling PDSCH in PCell
The first of the two downlink assignments. Nothing about it is CA specific except one field :
dai=1 = binary00 01 | | | +-- total DAI = 01 ->expect 2 PDSCHs in this feedback window +----- counter DAI = 00 ->this is the 1st of them
The gNB is telling the UE, in the very first DCI of the slot, that two acknowledgements will be needed. This matters because the UE has to build the HARQ codebook before it knows what else will be scheduled, and because DCIs get missed. If the UE had missed this DCI entirely, the total DAI in the next one would still tell it the codebook is 2 bits long, so it would send a NACK in the right bit position rather than sending a 1 bit codebook that the gNB cannot interpret. That robustness is the entire reason the DAI field exists.
Also
Message: ss_id=2 cce_index=6 al=2 dci=1_1
Data:
rb_alloc=0x0
time_domain_rsc=0
mcs1=19
ndi1=0
rv_idx1=0
harq_process=0
dai=1
tpc_command=1
pucch_rsc=0
harq_feedback_timing=2
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[8] PDSCH in PCell
92 bytes at 64QAM, code rate 0.87, symbols 1:13 as always, first transmission. This is the PCell's contribution to the slot. The only thing to note is
Message: harq=0 prb=0 symb=1:13 k1=6 CW0: tb_len=92 mod=6 rv_idx=0 cr=0.87 retx=0
[9] DCI(PDSCH) scheduling PDSCH in SCell
Here is the moment CA actually shows itself. This DCI is transmitted
dai=5 = binary01 01 | | | +-- total DAI = 01 -> still 2 PDSCHs in this window +----- counter DAI = 01 ->this is the 2nd of them
Put steps [7] and [9] side by side and the whole mechanism is visible in two numbers. The total DAI stayed at 01 in both, so the size of the codebook never changed. The counter DAI went 00 -> 01, so the UE knows the PCell PDSCH occupies bit 0 and the SCell PDSCH occupies bit 1. The UE now knows how to build a 2 bit codebook and where each bit goes, without ever needing to know which carrier is which - the ordering comes out of the DAI counting, not out of the cell index.
One more detail :
If you have the screenshot rather than the text log, none of this needs inferring. The Amarisoft trace has a
Message: ss_id=2 cce_index=0 al=2 dci=1_1
Data:
rb_alloc=0x0
time_domain_rsc=0
mcs1=21
ndi1=0
rv_idx1=0
harq_process=0
dai=5
tpc_command=1
pucch_rsc=0
harq_feedback_timing=2
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[10] PDSCH in SCell
101 bytes at
This is what carrier aggregation buys you, expressed in the smallest possible way : in this one slot the UE received 92 bytes on one carrier and 101 bytes on the other, and the two arrived independently on two separate 20 MHz channels.
Message: harq=0 prb=0 symb=1:13 k1=6 CW0: tb_len=101 mod=8 rv_idx=0 cr=0.71 retx=0
[11] HARQ Ack for PDSCH in both PCell and SCell
And here is the payoff.
Trace the chain backwards one last time, because it is the cleanest illustration of how CA holds together :
[7] DCI on PCell dai=1 -> counter 1 of 2 \
[8] PDSCH on PCell k1=6 |
|--> both point at the same uplink slot
[9] DCI on SCell dai=5 -> counter 2 of 2 |
[10] PDSCH on SCell k1=6 /
[11] one PUCCH on the PCell, ack=11 -> bit0 = PCell PDSCH, bit1 = SCell PDSCH
The SCell never transmitted anything. It has no uplink configuration at all, as we saw in step [1]. Every bit of feedback for it went out over the PCell, which is the arrangement the network prepared for back at the very beginning of this example when it added pucch-Resource 18 and the csi-ReportConfig with
Message: format=1 prb=50 prb2=0 symb=0:14 cs=0 occ=0 ack=11 snr=36.9 epre=-88.2
Example 02 > NR SA 2CC CA : n78 (20Mhz) + n78(20 Mhz) with Measurement Report
This example is from the log captured by Amarisoft Callbox(gNB) and UE Simulator. Overall procedure captured in this log is as follows.
| Step | Message | Layer / cell | Dir | What happens |
|---|---|---|---|---|
Measurement - the part Example 01 does not have |
||||
[1] |
RRC / PCell |
DL |
No SCell is added yet. Sets up measObject 2 + event A4, and a 6 ms measurement gap every 40 ms |
|
[2] |
RRC / PCell |
UL |
The UE starts using the gaps and evaluating A4 on the other carrier |
|
[3] |
RRC / PCell |
UL |
measId 1 fired. PCI 501 at about -90 dBm, SINR about 21 dB - good enough to add |
|
Establishment - identical to Example 01 steps [1] to [6] |
||||
[4] |
RRC / PCell |
DL |
Adds the SCell, and re-points measId 1 from event A4 to event A2 - from "should I add it" to "should I drop it" |
|
[5] |
RRC / PCell |
UL |
The SCell configuration is accepted |
|
[6] |
MAC / PCell |
DL |
Same 0x02 bitmap as Example 01. Only the padding length differs |
|
[7] |
PHY / PDCCH / PCell |
DL |
dai=0 : one PDSCH in this window, so a 1 bit HARQ codebook |
|
[8] |
PHY / PDSCH / PCell |
DL |
tb_len=92 bytes = 2 + 5 + 85, the MAC PDU of step [6] |
|
[9] |
PHY / PUCCH / PCell |
UL |
||
Traffic - identical to Example 01 steps [7] to [11] |
||||
[10] |
PHY / PDCCH / PCell |
DL |
dai=1 : the codebook has grown to 2 bits since step [7] |
|
[11] |
PHY / PDSCH / PCell |
DL |
92 bytes, 64QAM, k1=5 |
|
[12] |
PHY / PDCCH / |
DL |
dai=5 : counter DAI 2 of 2. But mcs1=7, far below the PCell's 19 |
|
[13] |
PHY / PDSCH / |
DL |
Only 34 bytes at 16QAM - the scheduler has no CSI from the SCell yet |
|
[14] |
PHY / PUCCH / PCell |
UL |
||
Before any of this, the first screenshot above shows something that is easy to scroll past but is the precondition for the entire example :
Compare this step table against Example 01's and you will see that steps [4] to [14] here are the same eleven steps as [1] to [11] there. Everything that is new is at the front : three extra messages that turn "the network decided to add an SCell" into "the network found out that adding an SCell was a good idea, and then added it". That is the difference between a lab configuration and how a real network behaves.
The build-up is worth stating before we look at the messages :
[1] the network tells the UE to go and look at the other carrier, and under what condition to report back[2] the UE confirms it will[3] the UE finds the other carrier is good enough and reports it[4] only now does the network add the SCell - and while it is at it, it changes the meaning of the measurement it had configured in [1]



[1] RrcReconfiguration for Scell Measurement
No sCellToAddModList anywhere in this message. Nothing is being added yet. This message only sets up measurements, and it does it in the four-part structure that every NR measConfig uses :
measObjectToAddModList what to measure (a frequency)reportConfigToAddModList when to report (an event and its threshold)measIdToAddModList which pairs of the above are actually activequantityConfig how to filter the raw measurementsmeasGapConfig when the UE is allowed to stop receiving and go look
measObjectId1 : ssbFrequency621216 (3318.24 MHz) - this is the PCell's own frequency measObjectId2 : ssbFrequency627264 (3408.96 MHz) - the candidate SCell frequency both : ssbSubcarrierSpacing kHz30, freqBandIndicatorNR 78 smtc1 periodicityAndOffset sf20: 0, duration sf1
The
reportConfigId1 :eventA2 - serving becomes worse than threshold a2-Threshold rsrp16 -> RSRP_LEV 16 = about-141 dBm hysteresis 0, timeToTrigger ms0, reportAmount r1 reportConfigId2 :eventA4 - neighbour becomes better than threshold a4-Threshold rsrp36 -> RSRP_LEV 36 = about-121 dBm hysteresis 0, timeToTrigger ms0, reportAmount r1 useWhiteCellList FALSE both : rsType ssb, reportInterval ms120, maxReportCells 1, reportQuantityCell { rsrp, rsrq, sinr }, includeBeamMeasurements FALSE
The RSRP values are reported as RSRP-Range indices, not dBm. The mapping is RSRP_LEV n corresponds to roughly (n - 157) dBm, so 16 is about -141 dBm and 36 is about -121 dBm.
And
measIdToAddModList :
measId 1 = measObjectId 2 + reportConfigId 2
(the other carrier) (event A4)
So of everything configured, only one measurement is running : watch frequency 627264 and tell me when a cell there exceeds about -121 dBm. measObjectId 1 (the PCell's own frequency) and reportConfigId 1 (event A2) are configured but idle. Networks routinely pre-load configurations like this so that later they can switch behaviour with a tiny delta reconfiguration instead of resending everything - and that is precisely what happens in step [4].
measGapConfig -> gapUE setup :
gapOffset 16 , mgl ms6 , mgrp ms40 , mgta ms0
-> a 6 ms gap every 40 ms , starting at subframe offset 16
Why does an
The cost is real and worth knowing : 6 ms out of every 40 ms is 15% of the UE's receive time given up. The network is deliberately sacrificing 15% of the current throughput for a few hundred milliseconds in order to find out whether it can double the throughput afterwards. Once the SCell is configured, the UE receives that carrier continuously and the gap is no longer needed for it.
{
message c1: rrcReconfiguration: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfiguration: {
measConfig {
measObjectToAddModList {
{
measObjectId 1,
measObject measObjectNR: {
ssbFrequency 621216,
ssbSubcarrierSpacing kHz30,
smtc1 {
periodicityAndOffset sf20: 0,
duration sf1
},
referenceSignalConfig {
ssb-ConfigMobility {
deriveSSB-IndexFromCell FALSE
}
},
quantityConfigIndex 1,
offsetMO {
},
freqBandIndicatorNR 78
}
},
{
measObjectId 2,
measObject measObjectNR: {
ssbFrequency 627264,
ssbSubcarrierSpacing kHz30,
smtc1 {
periodicityAndOffset sf20: 0,
duration sf1
},
referenceSignalConfig {
ssb-ConfigMobility {
deriveSSB-IndexFromCell FALSE
}
},
quantityConfigIndex 1,
offsetMO {
},
freqBandIndicatorNR 78
}
}
},
reportConfigToAddModList {
{
reportConfigId 1,
reportConfig reportConfigNR: {
reportType eventTriggered: {
eventId eventA2: {
a2-Threshold rsrp: 16,
reportOnLeave FALSE,
hysteresis 0,
timeToTrigger ms0
},
rsType ssb,
reportInterval ms120,
reportAmount r1,
reportQuantityCell {
rsrp TRUE,
rsrq TRUE,
sinr TRUE
},
maxReportCells 1,
includeBeamMeasurements FALSE
}
}
},
{
reportConfigId 2,
reportConfig reportConfigNR: {
reportType eventTriggered: {
eventId eventA4: {
a4-Threshold rsrp: 36,
reportOnLeave FALSE,
hysteresis 0,
timeToTrigger ms0,
useWhiteCellList FALSE
},
rsType ssb,
reportInterval ms120,
reportAmount r1,
reportQuantityCell {
rsrp TRUE,
rsrq TRUE,
sinr TRUE
},
maxReportCells 1,
includeBeamMeasurements FALSE
}
}
}
},
measIdToAddModList {
{
measId 1,
measObjectId 2,
reportConfigId 2
}
},
quantityConfig {
quantityConfigNR-List {
{
quantityConfigCell {
ssb-FilterConfig {
},
csi-RS-FilterConfig {
}
}
}
}
},
measGapConfig {
gapUE setup: {
gapOffset 16,
mgl ms6,
mgrp ms40,
mgta ms0
}
}
},
nonCriticalExtension {
masterCellGroup {
cellGroupId 0,
spCellConfig {
spCellConfigDedicated {
initialDownlinkBWP {
pdsch-Config setup: {
resourceAllocation resourceAllocationType1,
rbg-Size config1,
mcs-Table qam256,
prb-BundlingType staticBundling: {
bundleSize wideband
}
}
},
uplinkConfig {
initialUplinkBWP {
pusch-Config setup: {
txConfig codebook,
resourceAllocation resourceAllocationType1,
mcs-Table qam256,
mcs-TableTransformPrecoder qam256,
codebookSubset nonCoherent,
maxRank 1
}
}
},
tag-Id 0
}
}
},
dedicatedNAS-MessageList {
'7E022CCB5D1C017E0042010977000B...'H
}
}
}
}
}
[2] RrcReconfigurationComplete
The same empty acknowledgement as Example 01 step [2]. From this moment the UE starts using the measurement gaps and evaluating event A4 on frequency 627264.
{
message c1: rrcReconfigurationComplete: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfigurationComplete: {
}
}
}
[3] Measurement Report
The A4 condition was met and the UE reports. This is the message that triggers everything that follows. Note that the log here is truncated part way through the neighbour cell entry, but everything that matters is visible :
measId1 <-- this is the A4 measurement configured in step [1] measResultServingMOList : servCellId 0, physCellId500 (the PCell) rsrp70 -> about-87 dBm rsrq 65 -> about -10.5 dB sinr 84 -> about 19 dB measResultNeighCells measResultListNR : physCellId501 (the candidate SCell) rsrp67 -> about-90 dBm rsrq 65 -> about -10.5 dB sinr 88 -> about 21 dB
The first thing to check is
Then the numbers. The neighbour, PCI 501, is at about -90 dBm, comfortably above the A4 threshold of about -121 dBm, so the event fired. It is 3 dB weaker than the serving cell but that is irrelevant for A4 - and it is a good illustration of why A4 rather than A3 is the right event here. Under A3 the network would be asking "should I hand over to 501", the answer would be no, and no CA would ever be configured. Under A4 the question is "is 501 good enough to also use", and the answer is clearly yes.
The SINR of about 21 dB is what really matters for CA, because it predicts what MCS the SCell will support. At 21 dB the SCell should eventually reach 256QAM, and in Example 01 step [10] we saw a settled SCell doing exactly that.
{
message c1: measurementReport: {
criticalExtensions measurementReport: {
measResults {
measId 1,
measResultServingMOList {
{
servCellId 0,
measResultServingCell {
physCellId 500,
measResult {
cellResults {
resultsSSB-Cell {
rsrp 70,
rsrq 65,
sinr 84
}
}
}
}
}
},
measResultNeighCells measResultListNR: {
{
physCellId 501,
measResult {
cellResults {
resultsSSB-Cell {
rsrp 67,
rsrq 65,
sinr 88
}
}
[4] RrcReconfiguration for Scell Addition
The network has its answer and now adds the SCell. The bulk of this message - sCellToAddModList with sCellIndex 1, physCellId 501, the TDD pattern, the search space, the TRS, the CSI-RS and CSI-IM - is the same content that was explained in detail in Example 01 step [1], so I will not repeat it. What is worth studying here are the three things that are
Example 01 step [1], spCellConfigDedicated contained :
initialDownlinkBWP (pdsch-Config), uplinkConfig (pucch-Config and pusch-Config),
csi-MeasConfig, tag-Id
Example 02 step [4], spCellConfigDedicated contains :
uplinkConfig -> pucch-Config only (pucch-ResourceId 18, same as before)
csi-MeasConfig (reportConfigId 1, carrier 1 , pucch-Resource 18)
tag-Id 0
The pdsch-Config and pusch-Config are missing because they were already set correctly by an earlier reconfiguration and nothing about them needs to change. RRC configuration in NR is
measIdToAddModList :
measId 1 = measObjectId 2 + reportConfigId 1
compare with step [1] :
measId 1 = measObjectId 2 + reportConfigId 2
same measId, same measObject, different reportConfig
reportConfigId 2 = eventA4 (neighbour becomes better) -> "should I add this carrier?"
reportConfigId 1 = eventA2 (serving becomes worse) -> "should I drop this carrier?"
Read it in plain English. Before this message, frequency 627264 was a neighbour and the interesting question was whether it was good enough to add - so measId 1 was watching for A4. After this message, frequency 627264
This is why reportConfigId 1 was configured back in step [1] and then left unused. The network pre-loaded both halves of the SCell's life cycle, and switching between them costs one small measIdToAddModList entry instead of a full measurement reconfiguration. And it explains the A2 threshold that looked so strange in step [1] : about -141 dBm is essentially "never, unless the carrier disappears completely". In a lab you do not want the SCell dropping itself ; in a real network that threshold would be set somewhere sensible like -110 dBm so the SCell is released when it stops being worth its overhead.
sCellConfigDedicated :
...
tag-Id 0,
servingCellMO 2 <-- links this SCell to measObjectId 2
This ties the SCell to measObjectId 2, the measObject for frequency 627264. Without it, the UE would have no way to know that the cell it is now serving on that frequency is the same thing as the measObject it has been measuring. With it, measurements on the SCell are reported under measResultServingMOList as a serving cell rather than as a neighbour - which is exactly what event A2 needs in order to evaluate "the serving cell has become worse than". Example 01 had no measConfig at all, so it had nothing to link to and the field was correctly absent.
{
message c1: rrcReconfiguration: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfiguration: {
measConfig {
measIdToAddModList {
{
measId 1,
measObjectId 2,
reportConfigId 1
}
}
},
nonCriticalExtension {
masterCellGroup {
cellGroupId 0,
mac-CellGroupConfig {
phr-Config setup: {
phr-PeriodicTimer sf500,
phr-ProhibitTimer sf200,
phr-Tx-PowerFactorChange dB3,
multiplePHR FALSE,
dummy FALSE,
phr-Type2OtherCell FALSE,
phr-ModeOtherCG real
},
skipUplinkTxDynamic FALSE
},
spCellConfig {
spCellConfigDedicated {
uplinkConfig {
initialUplinkBWP {
pucch-Config setup: {
resourceToAddModList {
{
pucch-ResourceId 18,
startingPRB 49,
intraSlotFrequencyHopping enabled,
secondHopPRB 1,
format format2: {
nrofPRBs 1,
nrofSymbols 2,
startingSymbolIndex 2
}
}
}
}
}
},
csi-MeasConfig setup: {
csi-ReportConfigToAddModList {
{
reportConfigId 1,
carrier 1,
resourcesForChannelMeasurement 0,
csi-IM-ResourcesForInterference 1,
reportConfigType periodic: {
reportSlotConfig slots80: 18,
pucch-CSI-ResourceList {
{
uplinkBandwidthPartId 0,
pucch-Resource 18
}
}
},
reportQuantity cri-RI-PMI-CQI: NULL,
reportFreqConfiguration {
cqi-FormatIndicator widebandCQI
},
timeRestrictionForChannelMeasurements notConfigured,
timeRestrictionForInterferenceMeasurements notConfigured,
groupBasedBeamReporting disabled: {
},
cqi-Table table2,
subbandSize value1
}
}
},
tag-Id 0
}
},
sCellToAddModList {
{
sCellIndex 1,
sCellConfigCommon {
physCellId 501,
downlinkConfigCommon {
frequencyInfoDL {
absoluteFrequencySSB 627264,
frequencyBandList {
78
},
absoluteFrequencyPointA 626688,
scs-SpecificCarrierList {
{
offsetToCarrier 0,
subcarrierSpacing kHz30,
carrierBandwidth 51
}
}
},
initialDownlinkBWP {
genericParameters {
locationAndBandwidth 13750,
subcarrierSpacing kHz30
},
pdcch-ConfigCommon setup: {
controlResourceSetZero 10,
commonControlResourceSet {
controlResourceSetId 1,
frequencyDomainResources '011111110000000000000000000000000000000000000'B,
duration 1,
cce-REG-MappingType nonInterleaved: NULL,
precoderGranularity sameAsREG-bundle
},
searchSpaceZero 0,
commonSearchSpaceList {
{
searchSpaceId 1,
controlResourceSetId 0,
monitoringSlotPeriodicityAndOffset sl1: NULL,
monitoringSymbolsWithinSlot '10000000000000'B,
nrofCandidates {
aggregationLevel1 n0,
aggregationLevel2 n0,
aggregationLevel4 n4,
aggregationLevel8 n0,
aggregationLevel16 n0
},
searchSpaceType common: {
dci-Format0-0-AndFormat1-0 {
}
}
}
},
searchSpaceSIB1 0,
searchSpaceOtherSystemInformation 1,
pagingSearchSpace 1,
ra-SearchSpace 1
},
pdsch-ConfigCommon setup: {
pdsch-TimeDomainAllocationList {
{
mappingType typeA,
startSymbolAndLength 40
},
{
mappingType typeA,
startSymbolAndLength 57
}
}
}
}
},
ssb-PositionsInBurst mediumBitmap: '80'H,
ssb-periodicityServingCell ms20,
dmrs-TypeA-Position pos2,
ssbSubcarrierSpacing kHz30,
tdd-UL-DL-ConfigurationCommon {
referenceSubcarrierSpacing kHz30,
pattern1 {
dl-UL-TransmissionPeriodicity ms5,
nrofDownlinkSlots 7,
nrofDownlinkSymbols 6,
nrofUplinkSlots 2,
nrofUplinkSymbols 4
}
},
ss-PBCH-BlockPower -28
},
sCellConfigDedicated {
initialDownlinkBWP {
pdcch-Config setup: {
searchSpacesToAddModList {
{
searchSpaceId 2,
controlResourceSetId 1,
monitoringSlotPeriodicityAndOffset sl1: NULL,
monitoringSymbolsWithinSlot '10000000000000'B,
nrofCandidates {
aggregationLevel1 n0,
aggregationLevel2 n2,
aggregationLevel4 n1,
aggregationLevel8 n0,
aggregationLevel16 n0
},
searchSpaceType ue-Specific: {
dci-Formats formats0-1-And-1-1
}
}
}
},
pdsch-Config setup: {
dmrs-DownlinkForPDSCH-MappingTypeA setup: {
dmrs-AdditionalPosition pos1
},
tci-StatesToAddModList {
{
tci-StateId 0,
qcl-Type1 {
referenceSignal ssb: 0,
qcl-Type typeD
}
}
},
resourceAllocation resourceAllocationType1,
rbg-Size config1,
mcs-Table qam256,
prb-BundlingType staticBundling: {
bundleSize wideband
},
zp-CSI-RS-ResourceToAddModList {
{
zp-CSI-RS-ResourceId 0,
resourceMapping {
frequencyDomainAllocation row4: '100'B,
nrofPorts p4,
firstOFDMSymbolInTimeDomain 8,
cdm-Type fd-CDM2,
density one: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
periodicityAndOffset slots80: 1
}
},
p-ZP-CSI-RS-ResourceSet setup: {
zp-CSI-RS-ResourceSetId 0,
zp-CSI-RS-ResourceIdList {
0
}
}
}
},
firstActiveDownlinkBWP-Id 0,
pdcch-ServingCellConfig setup: {
},
pdsch-ServingCellConfig setup: {
nrofHARQ-ProcessesForPDSCH n16
},
csi-MeasConfig setup: {
nzp-CSI-RS-ResourceToAddModList {
{
nzp-CSI-RS-ResourceId 0,
resourceMapping {
frequencyDomainAllocation row2: '800'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density one: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots80: 1,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 1,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 11,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 2,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 8,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 11,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 3,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 4,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 12,
qcl-InfoPeriodicCSI-RS 0
},
{
nzp-CSI-RS-ResourceId 4,
resourceMapping {
frequencyDomainAllocation row1: '1'H,
nrofPorts p1,
firstOFDMSymbolInTimeDomain 8,
cdm-Type noCDM,
density three: NULL,
freqBand {
startingRB 0,
nrofRBs 52
}
},
powerControlOffset 0,
powerControlOffsetSS db0,
scramblingID 501,
periodicityAndOffset slots40: 12,
qcl-InfoPeriodicCSI-RS 0
}
},
nzp-CSI-RS-ResourceSetToAddModList {
{
nzp-CSI-ResourceSetId 0,
nzp-CSI-RS-Resources {
0
}
},
{
nzp-CSI-ResourceSetId 1,
nzp-CSI-RS-Resources {
1,
2,
3,
4
},
trs-Info true
}
},
csi-IM-ResourceToAddModList {
{
csi-IM-ResourceId 0,
csi-IM-ResourceElementPattern pattern1: {
subcarrierLocation-p1 s8,
symbolLocation-p1 8
},
freqBand {
startingRB 0,
nrofRBs 52
},
periodicityAndOffset slots80: 1
}
},
csi-IM-ResourceSetToAddModList {
{
csi-IM-ResourceSetId 0,
csi-IM-Resources {
0
}
}
},
csi-ResourceConfigToAddModList {
{
csi-ResourceConfigId 0,
csi-RS-ResourceSetList nzp-CSI-RS-SSB: {
nzp-CSI-RS-ResourceSetList {
0
}
},
bwp-Id 0,
resourceType periodic
},
{
csi-ResourceConfigId 1,
csi-RS-ResourceSetList csi-IM-ResourceSetList: {
0
},
bwp-Id 0,
resourceType periodic
},
{
csi-ResourceConfigId 2,
csi-RS-ResourceSetList nzp-CSI-RS-SSB: {
nzp-CSI-RS-ResourceSetList {
1
}
},
bwp-Id 0,
resourceType periodic
}
}
},
tag-Id 0,
servingCellMO 2
}
}
}
}
}
}
}
[5] RrcReconfigurationComplete
The SCell configuration is accepted. From here to the end of the example, everything follows Example 01 exactly - so rather than repeat the explanations, I will point out only where the numbers differ and why.
{
message c1: rrcReconfigurationComplete: {
rrc-TransactionIdentifier 0,
criticalExtensions rrcReconfigurationComplete: {
}
}
}
[6] MAC CE to activate the Scell
The same SCell Activation/Deactivation MAC CE as Example 01 step [3] :
Message: Scell AD:02 LCID:1 len=3 PAD: len=84
[7] DCI to schedule PDSCH carrying the MAC CE
On the PCell, as it must be, since the SCell is not on yet.
Message: ss_id=2 cce_index=0 al=2 dci=1_1
Data:
rb_alloc=0x32
time_domain_rsc=0
mcs1=19
ndi1=1
rv_idx1=0
harq_process=0
dai=0
tpc_command=1
pucch_rsc=0
harq_feedback_timing=3
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[8] PDSCH carrying the MAC CE
Message: harq=0 prb=50 symb=1:13 k1=5 CW0: tb_len=92 mod=6 rv_idx=0 cr=0.87 retx=0
[9] PUCCH (HARQ ACK) for PDSCH at step [8]
Keep this line in mind, because in five more steps it becomes ack=11 and that transition is the whole point of the example.
Message: format=1 prb=50 prb2=0 symb=0:14 cs=0 occ=0 ack=1 snr=37.1 epre=-87.9
[10] DCI (PDSCH) scheduling PDSCH in PCell
Message: ss_id=2 cce_index=0 al=2 dci=1_1
Data:
rb_alloc=0x32
time_domain_rsc=0
mcs1=19
ndi1=0
rv_idx1=0
harq_process=0
dai=1
tpc_command=1
pucch_rsc=0
harq_feedback_timing=3
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[11] PDSCH in PCell
92 bytes at 64QAM, code rate 0.87, symbols 1:13, k1=5. Business as usual on the PCell.
Message: harq=0 prb=50 symb=1:13 k1=5 CW0: tb_len=92 mod=6 rv_idx=0 cr=0.87 retx=0
[12] DCI(PDSCH) scheduling PDSCH in SCell
But look at the MCS :
Message: ss_id=2 cce_index=0 al=2 dci=1_1
Data:
rb_alloc=0x32
time_domain_rsc=0
mcs1=7
ndi1=1
rv_idx1=0
harq_process=0
dai=5
tpc_command=1
pucch_rsc=0
harq_feedback_timing=3
antenna_ports=0
srs_request=0
dmrs_seq_init=0
[13] PDSCH in SCell
This is the most instructive single line in Example 02.
| Ex 02 step [13] SCell just activated |
Ex 01 step [10] SCell settled |
|
|---|---|---|
MCS |
7 |
21 |
Modulation |
mod=4 (16QAM) |
mod=8 (256QAM) |
Code rate |
0.50 |
0.71 |
Transport block |
34 bytes |
101 bytes |
Three times the data on the same 20 MHz carrier, at the same measured radio quality. The difference is not the radio - the measurement report in step [3] said the SCell had about 21 dB SINR, which is plenty for 256QAM. The difference is
This is exactly what point (4) at the top of this page was about, now visible as a number in a log. If you are ever asked why a newly activated SCell "does not give the throughput it should", this is usually the answer, and the fix is in the configuration : a shorter CSI reporting period, or an aperiodic CSI request right after activation, gets the scheduler its information sooner.
Message: harq=0 prb=50 symb=1:13 k1=5 CW0: tb_len=34 mod=4 rv_idx=0 cr=0.50 retx=0
[14] HARQ Ack for PDSCH in both PCell and SCell
The single most useful thing in Example 02 is the transition you can now see across the whole log with one field :
step [9] ack=1 one serving cell -> 1 bit codebook (SCell configured, not yet active) step [14] ack=11 two serving cells -> 2 bit codebook (SCell active and scheduled)
If you only had time to look at two lines in a CA log to decide whether CA is actually working, these would be the two. An RRCReconfiguration with sCellToAddModList only proves the SCell was
Message: format=1 prb=50 prb2=0 symb=0:14 cs=0 occ=0 ack=11 snr=37.1 epre=-87.9
When CA does not work : where to look first
Having gone through both logs, the failure modes almost sort themselves. Here they are in the order I would check them, because that order is roughly from most common to least.
| Symptom in the log | Most likely cause | Where to look |
|---|---|---|
No measurement report ever arrives |
The smtc window does not match the target cell's SSB periodicity or offset, or the A4 threshold is set too high, or no measurement gap was given for a carrier the UE cannot reach without retuning. |
measObject smtc1, a4-Threshold, measGapConfig - Ex 02 step [1] |
Measurement report arrives but no SCell is added |
The UE's reported band combination does not support this pair, or the gNB has no CA licence or capacity configured for the second cell. Nothing in the air interface is wrong. |
UE Capability, gNB cell configuration |
RRCReconfiguration sent, no RRCReconfigurationComplete |
The UE could not apply the configuration and declared a reconfiguration failure. In SA this drops to idle - so also look for an RRC re-establishment or a fresh setup right after. |
the message after the missing Complete |
SCell configured, MAC CE sent, but no PDSCH ever appears on the SCell |
The scheduler has no data to send, or no CSI has come back so the SCell is never selected. Check whether the CSI report configured with carrier = the SCell index is actually arriving on the PCell's PUCCH. |
csi-ReportConfig carrier field and pucch-CSI-ResourceList - Ex 01 step [1] |
PDSCH on the SCell but throughput barely improves |
The SCell is stuck at a low MCS because CSI is slow or missing. See Ex 02 step [13]. Shorten the CSI period or add an aperiodic CSI trigger after activation. |
reportSlotConfig, TRS configuration |
HARQ ACK is never more than 1 bit |
The SCell is never actually scheduled in the same feedback window, or the DCI is format 1_0 rather than 1_1 - format 1_0 has no DAI and cannot carry a multi-cell codebook. |
dci= field and dai field in the DCI lines |
Frequent NACKs on the SCell only |
If both cells share a TAG but are not actually co-located, the SCell's timing is wrong. Check whether the SCell should have had its own tag-Id. |
tag-Id in sCellConfigDedicated |