Skip to content

Exceptionally high SNRs for SIONNA 3GPP UMa OFDM channels #769

Open
@mickwubs97

Description

@mickwubs97

I got what I think are exceptionally high SNRs with the SIONNA UMa channel model when simulating OFDM channels.
I am expecting 20 to -3 dB of SNRs across 1000 meters.
Is it because the TOTAL transmission power was allocated to 76 OFDM subcarriers?
Or is it normal (i.e., as expected for a 3GPP UMa)? I've been searching for references for SNR vs Distance curvatures under 3GPP UMa/UMi/RMa models but haven't found any.

Image

import sionna as sn
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from matplotlib import cm

# Define the number of UT and BS antennas
NUM_UT = 1
NUM_BS = 1
NUM_UT_ANT = 1
NUM_BS_ANT = 1

# The number of transmitted streams is equal to the number of UT antennas
# in both uplink and downlink
NUM_STREAMS_PER_TX = NUM_UT_ANT

# In this notebook, as we have only a single transmitter and receiver,
# the RX-TX association matrix is simply:
RX_TX_ASSOCIATION = np.array([[1]])

# Instantiate a StreamManagement object
# This determines which data streams are determined for which receiver.
# In this simple setup, this is fairly easy. However, it can get more involved
# for simulations with many transmitters and receivers.
STREAM_MANAGEMENT = sn.mimo.StreamManagement(RX_TX_ASSOCIATION, NUM_STREAMS_PER_TX)

# Setup resource grid and channel model
sm = sn.mimo.StreamManagement(np.array([[1]]), 1)
RESOURCE_GRID = sn.ofdm.ResourceGrid(num_ofdm_symbols=14,
                                     fft_size=76,
                                     subcarrier_spacing=30e3,
                                     num_tx=NUM_UT,
                                     num_streams_per_tx=NUM_STREAMS_PER_TX,
                                     cyclic_prefix_length=6,
                                     pilot_pattern="kronecker",
                                     pilot_ofdm_symbol_indices=[2, 11])
# RESOURCE_GRID.show()

CARRIER_FREQUENCY = 2.6e9  # Carrier frequency in Hz.
# This is needed here to define the antenna element spacing.

UT_ARRAY = sn.channel.tr38901.Antenna(polarization="single",
                                      polarization_type="V",
                                      antenna_pattern="38.901",
                                      carrier_frequency=CARRIER_FREQUENCY)
UT_ARRAY.show()

BS_ARRAY = sn.channel.tr38901.AntennaArray(num_rows=1,
                                           num_cols=NUM_BS_ANT,
                                           polarization="single",
                                           polarization_type="V",
                                           antenna_pattern="38.901",  # Try 'omni'
                                           carrier_frequency=CARRIER_FREQUENCY)
BS_ARRAY.show()
BATCH_SIZE = 200
distance = np.linspace(1, 1000, 250)
bs_loc = tf.zeros(shape=(BATCH_SIZE, NUM_BS, 3), dtype=tf.float32)
ut_locs = np.zeros(shape=(BATCH_SIZE, len(distance), 3))
for b in range(BATCH_SIZE):
    ut_locs[b, :, 1] = distance
ut_locs = tf.convert_to_tensor(ut_locs, dtype=tf.float32)
bs_ori = tf.zeros(shape=(BATCH_SIZE, NUM_BS, 3), dtype=tf.float32)
ut_ori = np.zeros(shape=(BATCH_SIZE, NUM_UT, 3))
ut_ori[:, :, 0] = np.pi
ut_ori = tf.convert_to_tensor(ut_ori, dtype=tf.float32)
ut_vol = tf.zeros(shape=(BATCH_SIZE, NUM_UT, 3), dtype=tf.float32)
in_state = tf.zeros(shape=(BATCH_SIZE, NUM_UT), dtype=tf.bool)

# power_tx_dbm = -20  # Downlink Power Spectrum Density (PSD)
# power_tx = 10**(power_tx_dbm/10)  # Tx power
# power_tx_sub = power_tx/RESOURCE_GRID.fft_size  # Tx power per subcarrier
# power_tx = (10 ** (power_tx_dbm / 10))*RESOURCE_GRID.subcarrier_spacing  # Tx power per symbol
power_tx = 1
k = 1.38*10**(-23)  # Boltzmann's constant
T = 293  # Absolute temperature in Kelvin
n0_sub = k*T*RESOURCE_GRID.subcarrier_spacing  # Noise power per subcarrier

SNRs = np.zeros(shape=(len(distance), RESOURCE_GRID.fft_size))

UMa = sn.channel.tr38901.UMa(carrier_frequency=3.5e9,
                             o2i_model='low',
                             ut_array=UT_ARRAY,
                             bs_array=BS_ARRAY,
                             direction='downlink',
                             enable_pathloss=True)

# Configure topology for channel model
UMa.set_topology(ut_locs, bs_loc, ut_ori, bs_ori, ut_vol, in_state=in_state, los=True)
# Generating OFDM channel
generate_channel = sn.channel.GenerateOFDMChannel(channel_model=UMa, resource_grid=RESOURCE_GRID)
# Generate a batch of channel responses
h_list = generate_channel()
check = tf.reduce_mean(tf.square(tf.abs(h_list)), axis=[0, 5])
# Apply OFDM channels
apply_channel = sn.channel.ApplyOFDMChannel(add_awgn=False)

for d in range(len(distance)):
    # Select a batch of channel responses
    h = h_list[:, d:d+1, :, :, :, :, :]
    # Generate channel inputs
    shape_in = (BATCH_SIZE, NUM_BS, NUM_BS_ANT, RESOURCE_GRID.num_ofdm_symbols, RESOURCE_GRID.fft_size)
    x_real = tf.random.uniform(shape_in, minval=0, maxval=2, dtype=tf.int32)  # Random real part
    x_real = tf.where(x_real == 0, -1, 1)
    x_real = np.sqrt(power_tx/2)*tf.cast(x_real, dtype=tf.float32)
    x_imag = tf.random.uniform(shape_in, minval=0, maxval=2, dtype=tf.int32)  # Random imaginary part
    x_imag = tf.where(x_imag == 0, -1, 1)
    x_imag = np.sqrt(power_tx/2)*tf.cast(x_imag, dtype=tf.float32)
    x = tf.complex(x_real, x_imag)
    # Generate noise variances
    shape_out = (BATCH_SIZE, NUM_UT, NUM_UT_ANT, RESOURCE_GRID.num_ofdm_symbols, RESOURCE_GRID.fft_size)
    no = n0_sub*np.ones(shape=shape_out, dtype=np.float32)
    y = apply_channel([x, h])
    no, y = np.squeeze(no), tf.squeeze(y).numpy()
    y_p = np.square(np.abs(y))
    snr_ins = 10*np.log10(np.divide(y_p, no))
    snr_ero = np.mean(snr_ins, axis=0)
    snr_ero = np.mean(snr_ero, axis=0)
    SNRs[d, :] = snr_ero
# Visualize results
X, Y = np.meshgrid(range(RESOURCE_GRID.fft_size), distance)
# Plot the surface
fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
surf = ax.plot_surface(X, Y, SNRs, vmin=SNRs.min(), cmap=cm.viridis)
fig.colorbar(surf, shrink=0.5, aspect=5)
ax.set_xlabel('frequency')
ax.set_ylabel('distance')
ax.set_zlabel('SNR (dB)')
# plt.show()

plt.figure()
plt.title("SNRs vs Distance")
for cid in range(RESOURCE_GRID.fft_size):
    plt.plot(distance, SNRs[:, cid])
plt.xlabel(r"distance (m)")
plt.ylabel(r"SNR (dB)")
plt.grid()
plt.show()

Could you please tell me where I did wrong in this code?
Thank you very much!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions