Performance comparison between different Positioning Methods for millimeter wave 5G Networks

5G supports multiple methods for positioning such as

  • TDoA based Positioning Methods

  • AoA based Positioning Methods

  • Hybrid Positioning based on TDoA + AoA

  • Hybrid Positioning based on ToA + AoA

Accuracy of time and angle measurements significantly varies depending on the terrain, cell-size, GDoP, link quality. But the one parameter that significantly degrades the POsitioning accuracy is lack of LoS links which results in in-sufficient accurate measurements. This is where Hybrid Positioning methods can improve the performance. The Hybrid Positioning based on TDoA + AoA and Hybrid Positioning based on ToA + AoA requires a minimum of 2 and 1 accurate LoS measurements respectively in comparison to 3, 4 for AoA and TDoA respectively for precise 2D positioning.

The accuracy of time and angle measurements significantly varies depending on the terrain, cell size, GDoP, and link quality. However, one parameter that significantly degrades positioning accuracy is the lack of Line of Sight (LoS) links, which results in insufficient accurate measurements. This is where hybrid positioning methods can improve performance. The hybrid positioning methods based on Time Difference of Arrival (TDoA) + Angle of Arrival (AoA) and Time of Arrival (ToA) + AoA require a minimum of 2 and 1 accurate LoS measurements, respectively, in comparison to 3 and 4 for AoA and TDoA, respectively, for precise 2D positioning.

Lets explore this performance in following project.

Table of Content

Import Libraries

Import Python Libraries

[1]:
# from IPython.display import display, HTML
# display(HTML("<style>.container { width:95% !important; }</style>"))

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

# %matplotlib widget
import matplotlib.pyplot  as plt
import matplotlib.patches as mpatches
import matplotlib as mpl

import numpy      as np
import numpy.matlib
import scipy      as sp
import scipy.io   as spio
import scipy.constants
from   scipy import interpolate

Import 5G Toolkit Libraries

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

from toolkit5G.ChannelModels      import AntennaArrays, SimulationLayout, ParameterGenerator, ChannelGenerator
from toolkit5G.ResourceMapping    import ResourceMapperSRS
from toolkit5G.ReceiverAlgorithms import ChannelEstimationSRS
from toolkit5G.Positioning        import ToAEstimation, DoAEstimation, PositionEstimation
from toolkit5G.ChannelProcessing  import AddNoise

Simulation Parameters

[3]:
propTerrain      = "InF-SH"         # Propagation Scenario or Terrain for BS-UE links
carrierFrequency = 28*10**9         # Array of two carrier frequencies in GHz
scs              = 120*10**3
Nfft             = 4096
numOfBSs         = np.array([6, 3]) # number of BSs
nBSs             = np.prod(numOfBSs)
nUEs             = 40              # number of UEs
numRBs           = 272
numSlots         = 1

Generate Wireless Channels

[4]:
## Generate the Wireless Channel

# Antenna Array at UE side
# assuming antenna element type to be "OMNI"
# with 2 panel and 2 single polarized antenna element per panel.
ueAntArray = AntennaArrays(antennaType = "OMNI",  centerFrequency = carrierFrequency, arrayStructure  = np.array([1,1,2,2,1]))
ueAntArray()

# # Radiation Pattern of Rx antenna element
# ueAntArray.displayAntennaRadiationPattern()


# Antenna Array at BS side
# assuming antenna element type to be "3GPP_38.901", a parabolic antenna
# with 4 panel and 4 single polarized antenna element per panel.
bsAntArray = AntennaArrays(antennaType = "3GPP_38.901", centerFrequency = carrierFrequency, arrayStructure  = np.array([1,1,8,4,1]))
bsAntArray()

# # Radiation Pattern of Tx antenna element
# bsAntArray[0].displayAntennaRadiationPattern()

# Layout Parameters
isd                  = 20            # inter site distance
minDist              = 0             # min distance between each UE and BS
ueHt                 = 1.5           # UE height
bsHt                 = 5             # BS height
bslayoutType         = "Rectangular" # BS layout type
ueDropType           = "Rectangular" # UE drop type
htDist               = "random"      # UE height distribution
ueDist               = "random"      # UE Distribution per site
nSectorsPerSite      = 1             # number of sectors per site
maxNumFloors         = 1             # Max number of floors in an indoor object
minNumFloors         = 1             # Min number of floors in an indoor object
heightOfRoom         = 5.1           # height of room or ceiling in meters
indoorUEfract        = 0.5           # Fraction of UEs located indoor
lengthOfIndoorObject = 3             # length of indoor object typically having rectangular geometry
widthOfIndoorObject  = 3             # width of indoor object
# forceLOS             = True        # boolen flag if true forces every link to be in LOS state
forceLOS             = False         # boolen flag if true forces every link to be in LOS state

# simulation layout object
simLayoutObj = SimulationLayout(numOfBS = numOfBSs,
                                numOfUE = nUEs,
                                heightOfBS = bsHt,
                                heightOfUE = ueHt,
                                ISD = isd,
                                layoutType = bslayoutType,
                                layoutWidth = 50,
                                layoutLength = 120,
                                ueDropMethod = ueDropType,
                                UEdistibution = ueDist,
                                UEheightDistribution = htDist,
                                numOfSectorsPerSite = nSectorsPerSite,
                                ueRoute = None)

simLayoutObj(terrain = propTerrain,
             carrierFreq = carrierFrequency,
             ueAntennaArray = ueAntArray,
             bsAntennaArray = bsAntArray,
             indoorUEfraction = indoorUEfract,
             heightOfRoom = heightOfRoom,
             lengthOfIndoorObject = lengthOfIndoorObject,
             widthOfIndoorObject = widthOfIndoorObject,
             forceLOS = forceLOS, clutterDensity=0.2, clutterHeight = 3)

#     paramGen = simLayoutObj.getParameterGenerator()


# displaying the topology of simulation layout
fig, ax = simLayoutObj.display2DTopology()
ax.set_xlabel("x-coordinates (m)")
ax.set_ylabel("y-coordinates (m)")
ax.set_title("Simulation Topology")
ax.axhline(y=-0.5*isd*numOfBSs[1], xmin=10/140, xmax=130/140, color="k")
ax.axhline(y= 0.5*isd*numOfBSs[1], xmin=10/140, xmax=130/140, color="k")
ax.axvline(x=-0.5*isd*numOfBSs[0], ymin=10/140, ymax=130/140, color="k")
ax.axvline(x= 0.5*isd*numOfBSs[0], ymin=10/140, ymax=130/140, color="k")

paramGen = simLayoutObj.getParameterGenerator(muLog10=-8, sigmaLog10=0.4)

# paramGen.displayClusters((0,0,0), rayIndex = 0)
channel  = paramGen.getChannel()
Hf       = channel.ofdm(scs, Nfft)[0]

Nt       = bsAntArray.numAntennas # Number of BS Antennas
Nr       = ueAntArray.numAntennas
Ntx      = bsAntArray.arrayStructure[3]
Nty      = bsAntArray.arrayStructure[2]

../../../_images/api_Projects_Project10_%5BSLS%5DHybrid_Positioning_in_IndoorFactory_5G_Networks_based_on_UL-TDoA_AoA_7_0.png

SRS Configurations

[5]:
## SRS Configurations
purpose                         = "positioning"
nrofSRS_Ports               = 1
transmissionComb    = 4
nrofSymbols             = 12
startPosition               = 2
repetitionFactor    = 1
nrOfCyclicShift             = 1
groupOrSequenceHopping      = "neither"
sequenceId                  = np.arange(nUEs)

systemFrameNumber       = 0
resourceType                    = "periodic"
subcarrierSpacing       = scs

bSRS                                = 0
cSRS                                = 61
bHop                                = 0

freqScalingFactor       = 1
startRBIndex                    = 0
enableStartRBHopping        = False
freqDomainShift         = 0
freqDomainPosition      = 0
srsPeriodicityInSlots       = 1
srsOffsetInSlots            = 0
betaSRS                     = 1
resourceGridSizeinRBs       = numRBs
Bandwidth               = resourceGridSizeinRBs*12*scs

Slot by Slot Simulation

  • Schedule a certain UEs for SRS transmission in each slot.

  • Beamform the slot Grid.

  • Pass the beamformed Grid through the wireless channel.

    • Consider inter-user interference.

  • Extract the resource Grid.

  • Estimate the channel between the scheduled users and each BS.

    • Estimate the channel using LS estimator.

    • Interpolate the channel for un-scheduled REs in the slot Grid.

  • Estimate the time of arrival (ToA) for each UE-BS link.

[6]:
print("***********  Transmission Grid Beamformed *********** ")
numRepetition    = 1
numSlotsPerFrame = np.int32(10*(15000/scs))
numUEsPerSlot    = transmissionComb
numSlots         = np.int32(np.ceil(nUEs*numRepetition/transmissionComb))
frameIndices     = np.int32(np.floor(np.arange(numUEsPerSlot*numRepetition)/transmissionComb)%numSlotsPerFrame)
slotIndices      = np.int32(np.floor(np.floor(np.arange(numUEsPerSlot*numRepetition)/transmissionComb)/numSlotsPerFrame))
combOffset       = np.int32(np.arange(numUEsPerSlot))



Lpath            = 2
ToAe             = np.zeros((nBSs,nUEs))

Lpath2           = 2
doaEst           = np.zeros((nBSs, nUEs, 2, Lpath2))
xoAEst           = np.zeros((nBSs, nUEs, 2))
# Create a ESPRIT DoA Object
espritDoa        = DoAEstimation(method="ESPRIT", Nr_x = Ntx, Nr_y = Nty, Nobservation = numRBs*12)

for ns in range(numSlots):

    ## SRS Grid Generation

    srsGrid      = np.zeros((numUEsPerSlot, 14, numRBs*12), dtype=np.complex64)
    srsObject    = np.empty((numUEsPerSlot), dtype=object)

    for nue in range(numUEsPerSlot):
        srsObject[nue]   = ResourceMapperSRS(nrofSRS_Ports, transmissionComb, nrofSymbols, startPosition,
                                             repetitionFactor, nrOfCyclicShift, groupOrSequenceHopping,
                                             sequenceId[nue], combOffset[nue], ns, frameIndices[nue],
                                             resourceType, purpose, subcarrierSpacing)

        srsGrid[nue] = srsObject[nue](bSRS, cSRS, bHop, freqScalingFactor, startRBIndex,
                                          enableStartRBHopping, freqDomainShift, freqDomainPosition,
                                          srsPeriodicityInSlots, srsOffsetInSlots, betaSRS,
                                          resourceGridSizeinRBs)[0,0,0]

    XGrid     = np.zeros((numUEsPerSlot, 14, Nfft), dtype=np.complex64)
    bwpOffset = np.random.randint(Nfft-resourceGridSizeinRBs*12)

    print("***********  SRS Grid Generated *********** ")
    ## Load the resource grid to Transmission Grid
    XGrid[...,bwpOffset:(bwpOffset+resourceGridSizeinRBs*12)] = srsGrid

    del srsGrid

    ## Beamforming
    # Beamforming angles
    # Inter-element spacing in vertical and horizontal
    Pt_dBm= 23
    Pt    = 10**(0.1*(Pt_dBm-30))
    lamda = 3*10**8/carrierFrequency
    d     = 0.5/lamda
    theta = 0
    # Wt    = np.sqrt(Pt/Nt)*np.exp(1j*2*np.pi*d*np.cos(theta)/(lamda*Nt)*np.arange(0,Nt))
    # Xf    = Wt.reshape(-1,1,1)*XGrid1
    Xf    = (transmissionComb*Pt/Nr)*XGrid[..., np.newaxis].repeat(Nr, axis = -1)
    del XGrid

    ueIndices     = np.arange(ns*numUEsPerSlot, (ns+1)*numUEsPerSlot)
    ## Pass through channel
    Yf = (Hf[:,:,ueIndices].transpose(1,2,0,3,5,4)@Xf[np.newaxis,...,np.newaxis]).sum(1)

    ## Add Noise
    BoltzmanConst = 1.380649*(10**(-23))
    temperature   = 300
    noiseFiguredBi= 7
    noiseFigure   = 10**(noiseFiguredBi/10)
    noisePower    = BoltzmanConst*temperature*scs/noiseFigure


#     noisePower    = 0
    kppm          = 0
    fCFO          = kppm*(np.random.rand()-0.5)*carrierFrequency*(10**(-6)); # fCFO = CFO*subcarrierSpacing
    CFO           = (fCFO/scs)/Nfft
    ##Yf            = AddNoise(True)(Y, noisePower, CFO)

    # Yf            = AddNoise(False)(Y, noisePower, 0)    #Added
    Yf            = np.complex64(Yf + np.sqrt(0.5*noisePower)*(np.random.standard_normal(Yf.shape) + 1j*np.random.standard_normal(Yf.shape)))

    ## Extract Resource Grid
    rxGrid = Yf[...,bwpOffset:(bwpOffset+resourceGridSizeinRBs*12),:,0].transpose(0,3,1,2)

    ## Channel Estimation and Interpolation
    Hfest = np.zeros((nBSs, numUEsPerSlot, Nt, 14, rxGrid.shape[-1]), dtype = np.complex64)
    chEST = ChannelEstimationSRS()
    chGrid = rxGrid.reshape(nBSs*Nt,14,-1)[:,np.newaxis,np.newaxis,np.newaxis]

    interpolatorType = "disabled" # "Spline", "Linear", "Cubic", "disabled"

    for nue in range(numUEsPerSlot):
#         print("UE-Index: "+str(ueIndices[nue])+" | slot-Index: "+str(ns))

        Hfest[:,nue] = chEST(chGrid, srsObject[nue], interpolatorType)[:,0,0,0].reshape(nBSs,Nt,14,-1)

    Hest = Hfest.sum(-2)/14


    ## ToA | DoA Estimation
    toaEstimation = ToAEstimation("ESPRIT", Hest[0, 0].T.shape)

    for nbs in range(nBSs):
        for nue in range(numUEsPerSlot):
#             print("(nbs, nue): ("+str(nbs)+", "+str(ueIndices[nue])+")")
            delayEstimates = np.sort(toaEstimation(Hest[nbs, nue].T,
                                                   Lpath,
                                                   subCarrierSpacing = scs))
            delayEstimates = delayEstimates[delayEstimates > 0]
            K = Lpath
            while((delayEstimates.size==0) or (delayEstimates[0]<=0 and K < 12)):
                K = K + 1
                delayEstimates = np.sort(toaEstimation(Hest[nbs, nue].T,
                                                       numberOfPath = K,
                                                       subCarrierSpacing = scs))
                delayEstimates = delayEstimates[delayEstimates > 0]
            if(delayEstimates.size == 0):
                ToAe[nbs, ueIndices[nue]] = 10**-9
            else:
                ToAe[nbs, ueIndices[nue]] = delayEstimates[0]


            # Ntx, Nty, dtx, dty will be taken from antenna arrays propoerties
            # Hk is estimated at the receiver.
            # However, for perfect CSI, it can be directly taken from channel.OFDM.

            doaEst[nbs, ueIndices[nue]] = espritDoa(Hest[nbs, nue], Lpath2, 0.5, 0.5)    # Return 2 x 5 DoA matrix
            # row-0 is azimuth angles for each path
            # row-1 is elevation angles for each path

    print("***********  ["+str(ns)+"]-Parameters Estimated *********** ")


***********  Transmission Grid Beamformed ***********
***********  SRS Grid Generated ***********
***********  [0]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [1]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [2]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [3]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [4]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
/home/tenet/Startup/Packages/5G_Toolkit/version15/Projects/14.Hybrid_Positioning_in_5G_Networks/../../toolkit5G/Positioning/Angle_Estimation/methods/espritDoA.py:104: RuntimeWarning: invalid value encountered in arcsin
  theta    = np.pi - np.arcsin(np.sqrt(np.abs(kx*ui)**2 + np.abs(ky*vi)**2)).reshape(1,-1)
***********  [5]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [6]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [7]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [8]-Parameters Estimated ***********
***********  SRS Grid Generated ***********
***********  [9]-Parameters Estimated ***********

Position Estimation: Based on UL-ToA

[7]:
## Position Estimation
rxPosition  = simLayoutObj.UELocations
txPosition  = simLayoutObj.BSLocations

# [TDoA]: Selection of k Most Accurate Measurements
k           = 4  # Select k-best measurements
error1      = (np.abs(ToAe-channel.delays[0,0,...,0])/channel.delays[0,0,...,0]) # Compute the ToA error in each measurement
bsIndices1  = (np.argsort(error1,axis=0)[0:k]).T


# [AoA]: Selection of k Most Accurate Measurements
xoA         = np.stack([paramGen.phiAoD_LoS[0], paramGen.thetaAoD_LoS[0]], axis = -1)
xoAEst      = doaEst[...,0]
txPosition  = simLayoutObj.BSLocations

k           = 4  # Select k-best measurements
error2      = np.abs(xoAEst - xoA).sum(-1) # Compute the DoA error in each measurement
bsIndices2  = (np.argsort(error2,axis=0)[0:k]).T

# [Hybrid-TDoA+AoA]: Selection of k Most Accurate Measurements
k           = 2  # Select k-best measurements
error31     = (np.abs(ToAe-channel.delays[0,0,...,0])/channel.delays[0,0,...,0]) # Compute the ToA error in each measurement
bsIndices31 = (np.argsort(error1,axis=0)[0:k]).T

k           = 3  # Select k-best measurements
error32     = np.abs(xoAEst - xoA).sum(-1) # Compute the DoA error in each measurement
bsIndices32 = (np.argsort(error2,axis=0)[0:k]).T

# [Hybrid-ToA+AoA]: Selection of k Most Accurate Measurements
k           = 2  # Select k-best measurements
error41     = (np.abs(ToAe-channel.delays[0,0,...,0])/channel.delays[0,0,...,0]) # Compute the ToA error in each measurement
bsIndices41 = (np.argsort(error1,axis=0)[0:k]).T

k           = 2  # Select k-best measurements
error42     = np.abs(xoAEst - xoA).sum(-1) # Compute the DoA error in each measurement
bsIndices42 = (np.argsort(error2,axis=0)[0:k]).T

# Position Estimation based on TDoA

posEstimator   = PositionEstimation()
# Position Estimation Object:
    # Positioning based on: TDoA
    # Optimization Method: Gradient Descent

rxPositionEstimate1 = np.zeros((nUEs, 2, 3))
rxPositionEstimate2 = np.zeros((nUEs, 3))
rxPositionEstimate3 = np.zeros((nUEs, 2, 3))
rxPositionEstimate4 = np.zeros((nUEs, 2, 3))


for nue in range(nUEs):
#     print("ue-Index: "+str(nue))
    toa  = ToAe[bsIndices1[nue],nue]
    tdoa = toa[1::] - toa[0]
    rxPositionEstimate1[nue] = posEstimator(txPosition[bsIndices1[nue]], tdoa = tdoa)

    rxPositionEstimate2[nue] = posEstimator(txPosition[bsIndices2[nue]], xoA = xoAEst[bsIndices2[nue],nue]*np.pi/180)

    toa  = ToAe[bsIndices31[nue],nue]
    tdoa = toa[1::] - toa[0]
    rxPositionEstimate3[nue] = posEstimator(txPosition[bsIndices31[nue]], tdoa = tdoa,
                                            xoA = xoAEst[bsIndices32[nue],nue]*np.pi/180,
                                            refPositionAoA = txPosition[bsIndices32[nue]])

    rxPositionEstimate4[nue] = posEstimator(txPosition[bsIndices41[nue]], toa = ToAe[bsIndices41[nue],nue],
                                            xoA = xoAEst[bsIndices42[nue],nue]*np.pi/180,
                                            refPositionAoA = txPosition[bsIndices42[nue]])

Visualization of Estimated Position

[8]:
## PSS Detection Plot
#################################################################
rxPosition  = simLayoutObj.UELocations
txPosition  = simLayoutObj.BSLocations
rangeEst_2D = np.sqrt(np.abs((ToAe*(3*10**8))**2 - (rxPosition[:,2].reshape(1,-1)-txPosition[:,2].reshape(-1,1))**2))

fig, ax = plt.subplots()
ax.set_aspect(True)
# fig, ax = simLayoutObj.display2DTopology(isEqualAspectRatio = True)
colors  = ["k","m","r","b","g","y","crimson"]

linestyle_tuple = ['solid', 'dotted', 'dashed', 'dashdot',
                   (0, (5, 10)),                # 'loosely dashed'
                   (0, (1, 10)),                # 'loosely dotted'
                   (5, (10, 3)),                # 'long dash with offset'
                   (0, (5, 1)),                 # 'densely dashed'
                   (0, (3, 10, 1, 10)),         # 'loosely dashdotted'
                   (0, (3, 5, 1, 5)),           # 'dashdotted'
                   (0, (3, 1, 1, 1)),           # 'densely dashdotted'
                   (0, (3, 5, 1, 5, 1, 5)),     # 'dashdotdotted'
                   (0, (3, 10, 1, 10, 1, 10)),  # 'loosely dashdotdotted'
                   (0, (3, 1, 1, 1, 1, 1))]     # 'densely dashdotdotted'

for nbs in range(k):
    for nue in range(nUEs):
            circle1 = plt.Circle((txPosition[bsIndices1[nue, nbs], 0], txPosition[bsIndices1[nue, nbs], 1]), rangeEst_2D[bsIndices1[nue, nbs], nue],
                                 color = colors[nue%7], lw = 0.2, ls = linestyle_tuple[nue%7], fill = False, zorder = 0)
            ax.add_artist(circle1)

ax.scatter(txPosition[:,0], txPosition[:,1], marker="P", color="b", edgecolors='white',
           s = 125, label="Tx-Locations", zorder = 3)
ax.scatter(rxPositionEstimate4[:,0,0], rxPositionEstimate4[:,0,1], marker="o", color="g",
           s = 75, label="Estimated Rx-Locations", zorder = 1)
ax.scatter(rxPosition[:,0], rxPosition[:,1], marker=".", color="r", edgecolors='white',
           s = 100, label="True Rx-Locations", zorder = 5)
ax.legend()
ax.set_xlabel("x-coordinates (m)")
ax.set_ylabel("y-coordinates (m)")
ax.set_title("Transmitter's Locations and Estimation Accuracy (True UE Location vs Estimated UE Locations)")
ax.set_xlim([-60, 60])
ax.set_ylim([-25, 25])
ax.grid(True)
plt.show()
../../../_images/api_Projects_Project10_%5BSLS%5DHybrid_Positioning_in_IndoorFactory_5G_Networks_based_on_UL-TDoA_AoA_15_0.png

Performance Analysis of Positioning Error for ToA based method

[11]:
fig, ax = plt.subplots(1,2, figsize=(15,6))
#######################################################################################################################
############################################ TDoA Positioning Error ###################################################
#######################################################################################################################

nbins = nUEs
xlimit = 1
ylimit = 1

posError2DTDoA = np.linalg.norm(rxPositionEstimate1[:,0, 0:2]-rxPosition[:, 0:2], axis=1)
posError3DTDoA = np.linalg.norm(rxPositionEstimate1[:,0]-rxPosition, axis=1)
posError3DTDoA = np.where(np.isnan(posError3DTDoA), posError2DTDoA+1.5, posError3DTDoA)

# Horizontal Error
count, bins_count = np.histogram(posError2DTDoA, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[UL-TDoA]-2D Positioning Error")

# Vertical Error
count, bins_count = np.histogram(posError3DTDoA, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[UL-TDoA]-3D Positioning Error")

#######################################################################################################################
############################################ AoA Positioning Error ####################################################
#######################################################################################################################

posError2DAoA = np.linalg.norm(rxPositionEstimate2[..., 0:2]-rxPosition[:, 0:2], axis=1)
posError3DAoA = np.linalg.norm(rxPositionEstimate2-rxPosition, axis=1)
posError3DAoA = np.where(np.isnan(posError3DAoA), posError2DAoA+1.5, posError3DAoA)

# Horizontal Error
count, bins_count = np.histogram(posError2DAoA, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[UL-AoA]-2D Positioning Error")

# 3D Error
count, bins_count = np.histogram(posError3DAoA, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[UL-AoA]-3D Positioning Error")


#######################################################################################################################
##################################### Hybrid [TDoA+AoA] Positioning Error #############################################
#######################################################################################################################
posError2DHybrid = np.linalg.norm(rxPositionEstimate3[:,0, 0:2]-rxPosition[:, 0:2], axis=1)
posError3DHybrid = np.linalg.norm(rxPositionEstimate3[:,0]-rxPosition, axis=1)
posError3DHybrid = np.where(np.isnan(posError3DHybrid), posError2DHybrid+1.5, posError3DHybrid)

# Horizontal Error
count, bins_count = np.histogram(posError2DHybrid, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[Hybrid:TDoA+AoA]-2D Positioning Error")


# Vertical Error
count, bins_count = np.histogram(posError3DHybrid, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[Hybrid:TDoA+AoA]-3D Positioning Error")


#######################################################################################################################
##################################### Hybrid [ToA+AoA] Positioning Error ##############################################
#######################################################################################################################
posError2DHybrid2 = np.linalg.norm(rxPositionEstimate4[:,0, 0:2]-rxPosition[:, 0:2], axis=1)
posError3DHybrid2 = np.linalg.norm(rxPositionEstimate4[:,0]-rxPosition, axis=1)
posError3DHybrid2 = np.where(np.isnan(posError3DHybrid2), posError2DHybrid2+1.5, posError3DHybrid2)

# Horizontal Error
count, bins_count = np.histogram(posError2DHybrid2, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[Hybrid:ToA+AoA]-2D Positioning Error")


# Vertical Error
count, bins_count = np.histogram(posError3DHybrid2, bins = nbins, range = [0, xlimit])
pdf = count/nUEs
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[Hybrid:ToA+AoA]-3D Positioning Error")

#######################################################################################################################

## Plot Properties
ax[0].set_xticks(np.linspace(0, xlimit, 11))
ax[0].set_xticks(np.linspace(0, xlimit, 21), minor=True)
ax[0].set_yticks(np.linspace(0, ylimit, 11))
ax[0].set_yticks(np.linspace(0, ylimit, 21), minor=True)
ax[0].set_xlabel("Positioning Error (m)")
ax[0].set_ylabel("CDF of Positioning Error")
ax[0].set_title("CDF of Positioning Error")
ax[0].axhline(y = 0.2, lw = 2, alpha = 1, linestyle = ':', color = "crimson", label = "50%ile Positioning Error line")
ax[0].axhline(y = 2/3, lw = 2, alpha = 1, linestyle = ':', color = "magenta", label = "66.6%ile Positioning Error line")
ax[0].axhline(y = 0.9, lw = 2, alpha = 1, linestyle = ':', color = "royalblue", label = "90%ile Positioning Error line")
ax[0].axvline(x = 0.4, lw = 2, alpha = 1, linestyle = '-', color = "midnightblue", label = "50 cm Positioning Accuracy line")

# Specify different settings for major and minor grids
ax[0].grid(which = 'minor', alpha = 0.25, linestyle = '--')
ax[0].grid(which = 'major', alpha = 1)

ax[0].set_xlim([0,xlimit])
ax[0].set_ylim([0,ylimit])
ax[0].legend()

ax[1].set_xticks(np.linspace(0, xlimit, 11))
ax[1].set_xticks(np.linspace(0, xlimit, 21), minor=True)
ax[1].set_yticks(np.linspace(0, ylimit, 11))
ax[1].set_yticks(np.linspace(0, ylimit, 21), minor=True)
ax[1].set_xlabel("Positioning Error (m)")
ax[1].set_ylabel("CDF of Positioning Error")
ax[1].set_title("CDF of Positioning Error")
ax[1].axhline(y = 0.2, lw = 2, alpha = 1, linestyle = ':', color = "crimson", label = "50%ile Positioning Error line")
ax[1].axhline(y = 2/3, lw = 2, alpha = 1, linestyle = ':', color = "magenta", label = "66.6%ile Positioning Error line")
ax[1].axhline(y = 0.9, lw = 2, alpha = 1, linestyle = ':', color = "royalblue", label = "90%ile Positioning Error line")
ax[1].axvline(x = 0.4, lw = 2, alpha = 1, linestyle = '-', color = "midnightblue", label = "50 cm Positioning Accuracy line")

# Specify different settings for major and minor grids
ax[1].grid(which = 'minor', alpha = 0.25, linestyle = '--')
ax[1].grid(which = 'major', alpha = 1)

ax[1].set_xlim([0,xlimit])
ax[1].set_ylim([0,ylimit])
ax[1].legend()

plt.show()
../../../_images/api_Projects_Project10_%5BSLS%5DHybrid_Positioning_in_IndoorFactory_5G_Networks_based_on_UL-TDoA_AoA_17_0.png

Positioning Results Averaged over 2000 UEs

[12]:
dB = np.load("Databases/Hybrid.npz")

posError3DTDoA           = dB["posError3DTDoA"]
posError2DTDoA           = dB["posError2DTDoA"]
posError3DAoA            = dB["posError3DAoA"]
posError2DAoA            = dB["posError2DAoA"]
posError3DHybridTDoA_AoA = dB["posError3DHybridTDoA_AoA"]
posError2DHybridTDoA_AoA = dB["posError2DHybridTDoA_AoA"]
posError3DHybridToA_AoA  = dB["posError3DHybridToA_AoA"]
posError2DHybridToA_AoA  = dB["posError2DHybridToA_AoA"]

fig, ax = plt.subplots(1,2, figsize=(15,6))
#######################################################################################################################
############################################ TDoA Positioning Error ###################################################
#######################################################################################################################

nbins = posError2DTDoA.size
xlimit = 0.2
ylimit = 1


# Horizontal Error
count, bins_count = np.histogram(posError2DTDoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[UL-TDoA]-2D Positioning Error")

# Vertical Error
nbins = posError3DTDoA.size
count, bins_count = np.histogram(posError3DTDoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[UL-TDoA]-3D Positioning Error")

#######################################################################################################################
############################################ AoA Positioning Error ####################################################
#######################################################################################################################

# Horizontal Error
nbins = posError2DAoA.size
count, bins_count = np.histogram(posError2DAoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[UL-AoA]-2D Positioning Error")

# 3D Error
nbins = posError3DAoA.size
count, bins_count = np.histogram(posError3DAoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[UL-AoA]-3D Positioning Error")


#######################################################################################################################
##################################### Hybrid [TDoA+AoA] Positioning Error #############################################
#######################################################################################################################

# Horizontal Error
nbins = posError2DHybridTDoA_AoA.size
count, bins_count = np.histogram(posError2DHybridTDoA_AoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[Hybrid:TDoA+AoA]-2D Positioning Error")


# Vertical Error
nbins = posError3DHybridTDoA_AoA.size
count, bins_count = np.histogram(posError3DHybridTDoA_AoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[Hybrid:TDoA+AoA]-3D Positioning Error")


#######################################################################################################################
##################################### Hybrid [ToA+AoA] Positioning Error ##############################################
#######################################################################################################################

# Horizontal Error
nbins = posError2DHybridToA_AoA.size
count, bins_count = np.histogram(posError2DHybridToA_AoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[0].plot(bins_count[1:], cdf, label = "[Hybrid:ToA+AoA]-2D Positioning Error")


# Vertical Error
nbins = posError3DHybridToA_AoA.size
count, bins_count = np.histogram(posError3DHybridToA_AoA, bins = nbins, range = [0, xlimit])
pdf = count/nbins
cdf = np.cumsum(pdf)
ax[1].plot(bins_count[1:], cdf, label = "[Hybrid:ToA+AoA]-3D Positioning Error")

#######################################################################################################################

## Plot Properties
ax[0].set_xticks(np.linspace(0, xlimit, 11))
ax[0].set_xticks(np.linspace(0, xlimit, 21), minor=True)
ax[0].set_yticks(np.linspace(0, ylimit, 11))
ax[0].set_yticks(np.linspace(0, ylimit, 21), minor=True)
ax[0].set_xlabel("Positioning Error (m)")
ax[0].set_ylabel("CDF of 2D Positioning Error")
ax[0].set_title("Comparison of 2D Positioning Performance of all the 4 methods")
ax[0].axhline(y = 0.2, lw = 2, alpha = 1, linestyle = ':', color = "crimson", label = "50%ile Positioning Error line")
ax[0].axhline(y = 2/3, lw = 2, alpha = 1, linestyle = ':', color = "magenta", label = "66.6%ile Positioning Error line")
ax[0].axhline(y = 0.9, lw = 2, alpha = 1, linestyle = ':', color = "royalblue", label = "90%ile Positioning Error line")
ax[0].axvline(x = 0.02, lw = 2, alpha = 1, linestyle = '-', color = "midnightblue", label = "2 cm Positioning Accuracy line")

# Specify different settings for major and minor grids
ax[0].grid(which = 'minor', alpha = 0.25, linestyle = '--')
ax[0].grid(which = 'major', alpha = 1)

ax[0].set_xlim([0,xlimit])
ax[0].set_ylim([0,ylimit])
ax[0].legend()

ax[1].set_xticks(np.linspace(0, xlimit, 11))
ax[1].set_xticks(np.linspace(0, xlimit, 21), minor=True)
ax[1].set_yticks(np.linspace(0, ylimit, 11))
ax[1].set_yticks(np.linspace(0, ylimit, 21), minor=True)
ax[1].set_xlabel("Positioning Error (m)")
ax[1].set_ylabel("CDF of 3D Positioning Error")
ax[1].set_title("Comparison of 3D Positioning Performance of all the 4 methods")
ax[1].axhline(y = 0.2, lw = 2, alpha = 1, linestyle = ':', color = "crimson", label = "50%ile Positioning Error line")
ax[1].axhline(y = 2/3, lw = 2, alpha = 1, linestyle = ':', color = "magenta", label = "66.6%ile Positioning Error line")
ax[1].axhline(y = 0.9, lw = 2, alpha = 1, linestyle = ':', color = "royalblue", label = "90%ile Positioning Error line")
ax[1].axvline(x = 0.05, lw = 2, alpha = 1, linestyle = '-', color = "midnightblue", label = "5 cm Positioning Accuracy line")

# Specify different settings for major and minor grids
ax[1].grid(which = 'minor', alpha = 0.25, linestyle = '--')
ax[1].grid(which = 'major', alpha = 1)

ax[1].set_xlim([0,xlimit])
ax[1].set_ylim([0,ylimit])
ax[1].legend()

plt.show()
../../../_images/api_Projects_Project10_%5BSLS%5DHybrid_Positioning_in_IndoorFactory_5G_Networks_based_on_UL-TDoA_AoA_19_0.png
[ ]: