Coverage Evaluation of Physical Broadcast Channels (PBCH) in 5G Non-Terrestrial Networks

The control channels are used to communicate the downlink control information (DCI) in 5G Networks. This control information is crucial for decoding the actual data blocks which is carried using the physical downlink shared channel (PDSCH). In this tutorial we will demonstrate the link-level and system-level performance of control channel for different aggregation levels. The aggregation level allows the base-station to control the amount of resources allocated to PDCCH for transitting the DCI.

Evaluation Methodology

Parameters

Values

Carrier frequency (\(f_c\))

2 GHz

Subcarrier spacing (\(\Delta f\))

15 kHz

Channel model/Terrain

NTN-CDL-A

FFT-size (\(N_{FFT}\))

4096

Bandwidth (\(B\))

5 MHz

Number of RBs (\(N_{RB}\))

24

Antenna Configurations

BS Antenna Configurations

[1,1,1,1,1]

UE Antenna Configurations

[1,1,1,1,1]

UE velocity

3 kmph

Antenna Configurations

Radius of Earth

6371 Km

Satellite Orbit

600 Km

Satellite Angle

30 Degree

Import Libraries

Import Python Libraries

[1]:
# %matplotlib widget
import matplotlib.pyplot as plt

import os
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'

import numpy as np
# from IPython.display import display, HTML
# display(HTML("<style>.container { width:90% !important; }</style>"))

Import 5G Libraries

[2]:
import sys
sys.path.append("../../")

from toolkit5G.SequenceGeneration import PSS, SSS, DMRS
from toolkit5G.PhysicalChannels   import PBCH, PBCHDecoder
from toolkit5G.ResourceMapping    import SSB_Grid, ResourceMapperSSB
from toolkit5G.ChannelModels      import AntennaArrays, ParameterGeneratorTDL

from toolkit5G.OFDM               import OFDMModulator, OFDMDemodulator

from toolkit5G.ChannelProcessing  import ApplyChannel, AddNoise
from toolkit5G.MIMOProcessing     import AnalogBeamforming, ReceiveCombining
from toolkit5G.ReceiverAlgorithms import PSSDetection, SSSDetection, ChannelEstimationAndEqualization, DMRSParameterDetection
from toolkit5G.ReceiverAlgorithms import ChannelEstimationAndEqualizationPBCH

from toolkit5G.Configurations     import TimeFrequency5GParameters, GenerateValidSSBParameters

Simulation Parameters

[3]:
terrain          = "NTN-TDL-C"         # Propagation Scenario or Terrain for BS-UE links
carrierFrequency = 2*10**9             # Carrier frequency 3.6 GHz
scs              = 15*10**3            # Subcarrier Spacing
nBatches         = 200                 # Number of Batches considered for simulation
numRBs           = 21                  # Please don't change this. The simulation will break down
Bandwidth        = 10*10**6            # System bandwidth
Nfft             = 1024                # FFT-size
bandwidthTx      = 10*(10**6);         # Transmission bandwidth
nSymbolFrame     = 140*int(scs/15000); # Number of OFDM symbols per frame (Its a function of subcarrier spacing)

nBSs             = 1                   # Number of BSs
nUEs             = nBatches            # Number of UEs

# Velocity of UEs is generated uniformly at randomly between [minVelocity, maxVelocity]
minVelocity      = 3*5/18              # Minimum velocity of the UEs
maxVelocity      = 3*5/18              # Maximum velocity of the UEs

txAntStruture    = np.array([1,1,1,1,1]) # Tx Antenna Structure
rxAntStruture    = np.array([1,1,1,1,1]) # Rx Antenna Structure
Nt               = txAntStruture.prod()
Nr               = rxAntStruture.prod()

nSectorsPerSite  = 1
print()
print("                  Terrain: "+terrain)
print("        Carrier Frequency: "+str(carrierFrequency/10**9)+" GHz")
print("       Subcarrier-Spacing: "+str(scs)+" kHz")
print("                Bandwidth: "+str(Bandwidth/10**6)+" MHz")
print("                 FFT-size: "+str(Nfft))
print("Number of Resource Blocks: "+str(nBatches))
print("Number of User Equipments: "+str(nUEs))
print("  Number of Base Stations: "+str(nBSs))
print("  Number of Base Antennas: "+str(Nt))
print("    Number of UE Antennas: "+str(Nr))

print()


                  Terrain: NTN-TDL-C
        Carrier Frequency: 2.0 GHz
       Subcarrier-Spacing: 15000 kHz
                Bandwidth: 10.0 MHz
                 FFT-size: 1024
Number of Resource Blocks: 200
Number of User Equipments: 200
  Number of Base Stations: 1
  Number of Base Antennas: 1
    Number of UE Antennas: 1

Generate NTN Channel

[4]:
ueAntArray = AntennaArrays(antennaType = "OMNI",  centerFrequency = carrierFrequency, arrayStructure  = rxAntStruture)
ueAntArray()

bsAntArray = AntennaArrays(antennaType = "3GPP_38.901", centerFrequency = carrierFrequency, arrayStructure  = txAntStruture)
bsAntArray()

nSnapShots               = 14
# timeInstances            = np.array([0])
timeInstances            = np.arange(28)/(28000)

radiusOfEarth            = 6371*10**3
satelliteAltitude        = 160*10**3
satelliteElevationAngle  = 50*np.pi/180
enableSpatialConsistency = False
correlationDistance      = 10
numSinusoids             = 1024
correlationMatrix        = None

print("       nSnapShots:"+str(nSnapShots))

paramGen  = ParameterGeneratorTDL(terrain, carrierFrequency = carrierFrequency, numTransmitter = nBSs, numReceiver = nUEs,
                                  timeInstances = timeInstances, minVelocity = minVelocity, maxVelocity = maxVelocity, radiusOfEarth = radiusOfEarth,
                                  satelliteAltitude = satelliteAltitude, satelliteElevationAngle = satelliteElevationAngle,
                                  enableSpatialConsistency = enableSpatialConsistency, correlationDistance = correlationDistance,
                                  numTxAntennas = Nt, numRxAntennas = Nr, numSinusoids = numSinusoids, correlationMatrix = correlationMatrix)

delaySpread   = 100*(10**-9)
kFactor       = None
muNLoSBias    = None
sigmaNLoSBias = None

paramGen(delaySpread = delaySpread, kFactor = kFactor, muNLoSBias = muNLoSBias, sigmaNLoSBias = sigmaNLoSBias)
channel   = paramGen.getChannels()

Hf        = channel.ofdm(subCarrierSpacing = scs, fftSize=Nfft, normalizeChannel=True)

print("     coefficients:"+str(channel.coefficients.shape))
print("           delays:"+str(channel.delays.shape))
print("               Hf:"+str(Hf.shape))
print()
       nSnapShots:14
     coefficients:(1, 28, 1, 200, 3, 1, 1)
           delays:(1, 1, 1, 1, 3)
               Hf:(1, 28, 1, 200, 1024, 1, 1)

Generate MIB and PBCH Configurations for NTN

[5]:
## This class fetches valid set of 5G parameters for the system configurations
tfParams         = TimeFrequency5GParameters(Bandwidth, scs, fftsize=Nfft)
tfParams(nSymbolFrame, typeCP = "normal")
nRB              = 12        # SSB Grid size (Number of RBs considered for SSB transition)
Neff             = tfParams.Neff        # Number of resource blocks for Resource Grid ( exclude gaurd band | offsets : BWP)
# Nfft             = 512                 # FFT-size for OFDM
lengthCP         = tfParams.lengthCP    # CP length


lamda                           = 3e8/carrierFrequency;
nSCSOffset                      = 1
ssbParameters                   = GenerateValidSSBParameters(carrierFrequency, nSCSOffset, ssbType="caseA",
                                                             nssbCandidatesInHrf = 4, isPairedBand = False,
                                                             withSharedSpectrumChannelAccess = False)

systemFrameNumber               = ssbParameters.systemFrameNumber
subCarrierSpacingCommon         = ssbParameters.subCarrierSpacingCommon
ssbSubCarrierOffset             = ssbParameters.ssbSubCarrierOffset
DMRSTypeAPosition               = ssbParameters.DMRSTypeAPosition
controlResourceSet0             = ssbParameters.controlResourceSet0
searchSpace0                    = ssbParameters.searchSpace0

isPairedBand                    = ssbParameters.isPairedBand
nSCSOffset                      = ssbParameters.nSCSOffset
choiceBit                       = ssbParameters.choiceBit
ssbType                         = ssbParameters.ssbType
nssbCandidatesInHrf             = ssbParameters.nssbCandidatesInHrf
ssbIndex                        = ssbParameters.ssbIndex
hrfBit                          = ssbParameters.hrfBit
cellBarred                      = ssbParameters.cellBarred
intraFrequencyReselection       = ssbParameters.intraFrequencyReselection
withSharedSpectrumChannelAccess = ssbParameters.withSharedSpectrumChannelAccess

nFrames                         = 0.5
Nsc_ssb                         = 240
Nsymb_ssb                       = 4
#_______________________________________

PSS, SSS, PBCH, PBCH-DMRS and SSB Generation

[6]:
N_ID2        = np.arange(3)

# Generate PSS sequence
pssObject    = PSS(N_ID2);
pssSequence  = pssObject()

N_ID1        = 0
N_ID         = 3*N_ID1 + N_ID2

# Generate SSS sequence
sssObject    = SSS(N_ID1, N_ID2);
sssSequence  = sssObject()

ssbIndex     = np.arange(nssbCandidatesInHrf)

# Generate DMRS sequence
dmrsLen      = 144;
dmrsObject   = DMRS("PBCH", N_ID.repeat(nssbCandidatesInHrf), np.tile(np.arange(nssbCandidatesInHrf), N_ID.size), nssbCandidatesInHrf, hrfBit)
# dmrsSeq = dmrs.getSequence("tensorflow")
dmrsSequence = dmrsObject(dmrsLen).reshape(N_ID.size,nssbCandidatesInHrf,-1)


# Generate PBCH symbols
pbchSymbols = np.zeros((nSectorsPerSite, nssbCandidatesInHrf, 432), dtype=np.complex64)
ssb         = np.zeros((nSectorsPerSite, nssbCandidatesInHrf, 4, 240), dtype=np.complex64)

for i in range(nSectorsPerSite):
    pbchObject   = PBCH(carrierFrequency, choiceBit, subCarrierSpacingCommon, DMRSTypeAPosition,
                        controlResourceSet0, searchSpace0, cellBarred,
                        intraFrequencyReselection, systemFrameNumber, ssbSubCarrierOffset,
                        hrfBit, ssbIndex, N_ID[i], nssbCandidatesInHrf)

    pbchSymbols[i] = pbchObject()

    ssbObject      = SSB_Grid(N_ID[i])
    ssb[i]         = ssbObject(pssSequence[i], sssSequence[i],
                               dmrsSequence[i], pbchSymbols[i])

fig, ax = ssbObject.displayGrid()
../../../_images/api_Projects_Project5_Coverage_Evaluations_for_SSB_PBCH_for_5G_NTN_11_0.png

Transmission OFDM Resource Grid

[7]:
numSymbols = 28
Pt         = 1
XGrid      = np.zeros((nBSs, Nt, numSymbols, 960), dtype = np.complex64)
Xf         = np.zeros((nBSs, Nt, numSymbols, Nfft),      dtype = np.complex64)


## Loading SSB to SSB Grid
#####################################
# ssbPositionInBurst = np.ones(nssbCandidatesInHrf, dtype=int)
ssbPositionInBurst    = np.ones(nssbCandidatesInHrf, dtype=int)

ssbRGobject  = ResourceMapperSSB(ssbType, carrierFrequency, isPairedBand, withSharedSpectrumChannelAccess)

antLocations = bsAntArray.locations[0,0].reshape(-1,3)
azimuth      = np.arange(0, nssbCandidatesInHrf)*(2*np.pi/3)/nssbCandidatesInHrf
secAngle     = np.arange(nSectorsPerSite)*(2*np.pi/3)
theta        = 95*np.pi/180

scOffset     = 240*np.arange(nssbCandidatesInHrf)

for nbs in range(nBSs):

    ssbGrid = ssbRGobject(ssb[nbs], ssbPositionInBurst, offsetInSubcarriers = 0,
                          offsetInRBs = 0, numRBs = numRBs)

    for nbm in range(nssbCandidatesInHrf):

        phi = azimuth[nbm] + secAngle[nbs%nSectorsPerSite]
        steeringVector = np.stack((np.sin(theta, dtype = np.float32)*np.cos(phi, dtype = np.float32),
                                   np.sin(theta, dtype = np.float32)*np.sin(phi, dtype = np.float32),
                                   np.cos(theta, dtype = np.float32)), axis = -1)[...,np.newaxis]

        beamVectors    = (antLocations@steeringVector)/lamda
        beamVectors    = np.sqrt(Pt/Nt)*np.exp(-1j*2*np.pi*beamVectors)

        n      = ssbRGobject.symbolIndices[nbm]

        numSCs = 240
        XGrid[nbs,:,n:n+4,scOffset[nbm]:scOffset[nbm]+numSCs] = beamVectors[...,np.newaxis]*(ssbGrid[np.newaxis,n:n+4,0:numSCs])

# bwpOffset    = np.random.randint(Nfft - numRBs*12)
bwpOffset    = 0

Xf[...,bwpOffset:bwpOffset+960] = XGrid

Pass through the Wireless Channel

[8]:
Yf = (Hf[0,0,:,:,np.newaxis]@Xf.transpose(0,2,3,1)[:,np.newaxis,...,np.newaxis])[...,0]

Heatmap of Received Grid

[9]:
ui = np.random.randint(nUEs)
bi = np.random.randint(nBSs)
ai = np.random.randint(Nr)

fig, ax = plt.subplots()
ax.imshow(np.abs(Yf[bi,ui,:,:,ai]).T, cmap = "hot", interpolation= "nearest", aspect='auto')
plt.show()
../../../_images/api_Projects_Project5_Coverage_Evaluations_for_SSB_PBCH_for_5G_NTN_17_0.png

Displaying the Received Noisy Resource Grid

[11]:
ui = np.random.randint(nUEs)
bi = np.random.randint(nBSs)
ai = np.random.randint(Nr)

fig, ax = plt.subplots()
ax.imshow(np.abs(Yr[bi,ui,:,:,ai]).T, cmap = "hot", interpolation= "nearest", aspect='auto')
plt.show()
../../../_images/api_Projects_Project5_Coverage_Evaluations_for_SSB_PBCH_for_5G_NTN_21_0.png

Displaying Noisy SSB Grid

[12]:
ui = np.random.randint(nUEs)


fig, ax = plt.subplots()
ax.imshow(np.abs(ssbEst[ui]).T, cmap = "hot", interpolation= "nearest", aspect='auto')
plt.show()
../../../_images/api_Projects_Project5_Coverage_Evaluations_for_SSB_PBCH_for_5G_NTN_23_0.png

Performance Evaluations: SNR vs BLER

[13]:
fig, ax = plt.subplots()

ax.semilogy(SNRdB, bler, "-b", marker = "o", label = "PBCH-BLER")
plt.legend(loc="best")

ax.set_xticks(SNRdB)
ytck = (0.1**(np.arange(1, 4))).repeat(9)*np.tile(np.arange(10, 1,-1), [3])
ytck = np.concatenate([[1],ytck])
ax.set_yticks(ytck, minor=True)
ax.set_yticks(0.1**(np.arange(0, 4)), minor=False)
ax.set_ylim([0.01,1.1])
ax.set_title('SNR (dB)vs BLER Performance: NTN')
ax.set_xlabel('SNR (dB)')
ax.set_ylabel('Block Error Rate (BLER)')

ax.grid(which = 'minor', alpha = 0.25, linestyle = '--')
ax.grid(which = 'major', alpha = 1)
plt.show()

# fig.savefig("PBCH_LLS_NTN.png", transparent = True, format = "png")
# fig.savefig("PBCH_LLS_NTN.svg", transparent = True, format = "svg")
../../../_images/api_Projects_Project5_Coverage_Evaluations_for_SSB_PBCH_for_5G_NTN_25_0.png
[ ]:

[ ]: