目录
- Sionna:用于 6G 物理层研究的开源库
- 主要特点
- 实现6G研究的民主化
- 支持 5G、6G 等
- 模块化、可扩展、可伸缩
- 快速启动您的研究
- 好处
- 原生人工智能支持
- 综合研究平台
- 开放生态系统
- 安装
- 笔记
- 使用 pip 安装
- 基于Docker的安装
- 从源代码安装
- “你好世界!”
- 探索锡奥纳 (Sionna)
- 加载所需包
- 首次链路级仿真
- 建立端到端模型
- 运行一些吞吐量测试(图形模式)
- 误码率 (BER) 蒙特卡罗模拟
- 结论
Sionna:用于 6G 物理层研究的开源库
Sionna ™是一个用于链路级仿真的 GPU 加速开源库。它能够快速构建复杂通信系统架构的原型,并为机器学习在 6G 信号处理中的集成提供原生支持。
Sionna ™ 是一个基于TensorFlow的开源库,用于模拟无线和光通信系统的物理层。复杂通信系统架构的快速原型设计就像连接所需的构建块一样简单,这些构建块以Keras层的形式提供。使用可微分层,梯度可以在整个系统中反向传播,这是系统优化和机器学习,尤其是神经网络集成的关键推动因素。NVIDIA GPU 加速提供了数量级更快的模拟,从而实现了对此类系统的交互式探索,例如,在可以在Google Colab等云服务上运行的Jupyter 笔记本中。如果没有可用的 GPU,Sionna 将在 CPU 上运行。
Sionna 由 NVIDIA 开发、不断扩展和使用,以推动 5G 和 6G 研究。它支持 MU-MIMO(多用户多输入多输出)链路级模拟设置,具有符合 5G 的代码,包括低密度奇偶校验 (LDPC) 和 Polar 编码/解码器、3GPP 信道模型、OFDM(正交频分复用)、信道估计、均衡和软解映射。还有许多其他组件可用,例如卷积码和 Turbo 码、用于模拟光纤信道的分步傅里叶方法以及用于研究单载波波形的滤波器和窗口。每个构建块都是一个独立的模块,可以根据您的需求轻松测试、理解和修改。
主要特点
实现6G研究的民主化
Sionna 实现了一系列经过精心测试的先进算法,可用于基准测试和端到端性能评估。这让您可以专注于研究,使其更具影响力和可重复性,同时减少花在实现专业领域之外的组件上的时间。Sionna 是下一代通信系统(如 6G)物理层研究的宝贵工具。
支持 5G、6G 等
Sionna 由 NVIDIA 开发、不断扩展和使用,用于推动 5G 和 6G 物理层研究。它支持越来越多的功能,例如使用符合 5G 标准的低密度奇偶校验 (LDPC) 和 Polar 码进行多用户多输入多输出 (MU-MIMO) 链路级模拟、3GPP TR38.901 信道模型和射线追踪、正交频分复用 (OFDM)、信道估计等。
模块化、可扩展、可伸缩
每个构建块都是一个独立的模块,您可以根据需要轻松测试、理解和修改。Sionna 提供高级 Python 应用程序编程接口 (API),可轻松建模复杂的通信系统,同时提供充分的灵活性以适应您的研究。基于 TensorFlow,Sionna 可自动跨多个 GPU 扩展。
快速启动您的研究
Sionna 附带大量文档,包括许多可帮助您尽快入门的教程。Sionna 开箱即用,支持 NVIDIA GPU,速度极快,非常适合通信领域的机器学习研究。
好处
原生人工智能支持
Sionna 是第一个完全可微分的链接级模拟器,它使神经网络的集成变得轻而易举。
综合研究平台
综合研究平台
Sionna 将链路级和通道模拟功能与原生机器学习和 GPU 支持相结合。
开放生态系统
Sionna 是基于 Python 的开源项目,欢迎第三方的贡献。
安装
Sionna 需要Python和Tensorflow。为了在您的机器上运行教程笔记本,您还需要JupyterLab 。您也可以在Google Colab上测试它们。虽然不是必需的,但我们建议在Docker 容器中运行 Sionna 。
笔记
Sionna 需要TensorFlow 2.13-2.15和 Python 3.8-3.11。我们推荐使用 Ubuntu 22.04。早期版本的 TensorFlow 可能仍能使用,但由于已知未修补的 CVE,因此不推荐使用。
要在 CPU 上运行光线追踪器,DrJit 需要LLVM 。请查看LLVM 后端的安装说明。光线追踪预览需要最新版本的JupyterLab 。您可以通过(需要重新启动JupyterLab)升级到最新版本。pip install --upgrade ipykernel jupyterlab
我们参考TensorFlow GPU 支持教程来了解 GPU 支持和所需的驱动程序设置。
使用 pip 安装
我们建议在虚拟环境中执行此操作,例如使用conda。在 macOS 上,您需要先安装tensorflow-macos。
1.)安装软件包
pip install sionna
2.)在 Python 中测试安装
python>>> import sionna
>>> print(sionna.__version__)
0.19.2
3.) 一旦安装了 Sionna,您就可以运行Sionna“Hello, World!”示例,查看快速入门指南或教程。
对于本地安装,可以使用JupyterLab Desktop应用程序。这直接包括 Python 的安装和配置。
基于Docker的安装
1.) 确保你的系统上安装了Docker 。在 Ubuntu 22.04 上,你可以运行例如
sudo apt install docker.io
确保您的用户属于docker组(请参阅Docker 安装后)。
sudo usermod -aG docker $USER
注销并重新登录以加载更新的群组成员资格。
为了在 Linux 上获得 GPU 支持,您需要安装NVIDIA Container Toolkit。
2.) 构建 Sionna Docker 映像。在 Sionna 目录中,运行:
make docker
3.) 运行支持 GPU 的 Docker 镜像
make run-docker gpus=all
或者不使用 GPU:
make run-docker
这将立即启动安装了 Sionna 的 Docker 映像,并在端口 8888 上运行 JupyterLab。
4.) 通过在浏览器中连接http://127.0.0.1:8888浏览示例笔记本。
从源代码安装
我们建议在虚拟环境中执行此操作,例如使用conda。
1.) 克隆此存储库并从其根文件夹中执行:
make install
2.)在 Python 中测试安装
python
>>> import sionna
>>> print(sionna.__version__)
0.19.2
“你好世界!”
导入 Sionna:
import os
if os.getenv("CUDA_VISIBLE_DEVICES") is None:gpu_num = 0 # Use "" to use the CPUos.environ["CUDA_VISIBLE_DEVICES"] = f"{gpu_num}"
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'# Import Sionna
try:import sionna
except ImportError as e:# Install Sionna if package is not already installedimport osos.system("pip install sionna")import sionna# IPython "magic function" for inline plots
%matplotlib inline
import matplotlib.pyplot as plt
让我们首先创建一个BinarySource来生成一批随机的位向量,我们可以将其映射到星座符号:
batch_size = 1000 # Number of symbols we want to generate
num_bits_per_symbol = 4 # 16-QAM has four bits per symbol
binary_source = sionna.utils.BinarySource()
b = binary_source([batch_size, num_bits_per_symbol])
b
<tf.Tensor:形状=(1000,4),dtype=float32,numpy=
数组([[0., 1., 0., 1.],[0., 0., 1., 0.],[1., 1., 0., 0.],...,[0., 0., 1., 0.],[1., 0., 1., 1.],[1., 0., 1., 1.]], dtype=float32)>
接下来,让我们创建一个星座并将其可视化:
constellation = sionna.mapping.Constellation("qam", num_bits_per_symbol)
constellation.show();
我们现在需要一个映射器,根据上面显示的位标记将 b 的每一行映射到星座符号。
mapper = sionna.mapping.Mapper(constellation=constellation)
x = mapper(b)
x[:10]
<tf.Tensor:形状=(10,1),dtype=complex64,numpy=
数组([[ 0.3162278-0.9486833j],[0.9486833+0.3162278j],[-0.3162278-0.3162278j],[0.9486833+0.3162278j],[0.9486833-0.9486833j],[0.3162278+0.9486833j],[0.9486833+0.9486833j],[0.3162278+0.9486833j],[0.3162278-0.3162278j],[ 0.3162278 + 0.9486833j]], dtype=complex64)>
现在让我们让事情变得更有趣一点,通过AWGN 信道发送我们的符号:
awgn = sionna.channel.AWGN()
ebno_db = 15 # Desired Eb/No in dB
no = sionna.utils.ebnodb2no(ebno_db, num_bits_per_symbol, coderate=1)
y = awgn([x, no])# Visualize the received signal
import matplotlib.pyplot as plt
import numpy as npfig = plt.figure(figsize=(7,7))
ax = fig.add_subplot(111)
plt.scatter(np.real(y), np.imag(y));
ax.set_aspect("equal", adjustable="box")
plt.xlabel("Real Part")
plt.ylabel("Imaginary Part")
plt.grid(True, which="both", axis="both")
plt.title("Received Symbols");
探索锡奥纳 (Sionna)
本示例笔记本将引导您了解基本原理并说明Sionna的主要功能。只需几个命令,您就可以模拟许多符合 5G 标准的组件的 PHY 层链路级性能,包括轻松可视化结果。
加载所需包
必须安装Sionna python 包。
import os
if os.getenv("CUDA_VISIBLE_DEVICES") is None:gpu_num = 0 # Use "" to use the CPUos.environ["CUDA_VISIBLE_DEVICES"] = f"{gpu_num}"
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'# Import Sionna
try:import sionna
except ImportError as e:# Install Sionna if package is not already installedimport osos.system("pip install sionna")import sionnaimport numpy as np
import tensorflow as tf
# Avoid warnings from TensorFlow
tf.get_logger().setLevel('ERROR')# IPython "magic function" for inline plots
%matplotlib inline
import matplotlib.pyplot as plt
提示:您可以通过操作员在 Jupyter 中运行 bash 命令!。
!nvidia-smi
2024 年 9 月 26 日星期四 14:40:00
+------------------------------------------------------------------------------------------------+
| NVIDIA-SMI 535.183.01 驱动程序版本:535.183.01 CUDA 版本:12.2 |
|----------------------------------------+----------------------+--------------------------------+
| GPU 名称 Persistence-M | Bus-Id Disp.A | 易失性 Uncorr. ECC |
| 风扇温度性能功率:使用情况/容量 | 内存使用情况 | GPU-Util 计算 M。|
| | | 米格 M. |
|=========================================+========================+======================|
| 0 NVIDIA GeForce RTX 3090 关闭 | 00000000:01:00.0 关闭 | N/A |
| 0% 48C P2 39W / 350W | 292MiB / 24576MiB | 0% 默认 |
| | | 不适用|
+----------------------------------------------------+----------------------+--------------------------------+
| 1 NVIDIA GeForce RTX 3090 关闭 | 00000000:4D:00.0 关闭 | N/A |
| 30% 42C P8 23W / 350W | 3MiB / 24576MiB | 0% 默认 |
| | | 不适用|
+----------------------------------------------------+----------------------+--------------------------------++------------------------------------------------------------------------------------------------+
| 流程: |
| GPU GI CI PID 类型 进程名称 GPU 内存 |
| ID ID使用情况 |
|=============================================================================================|
+------------------------------------------------------------------------------------------------+
如果有多个 GPU 可用,我们会将此笔记本限制为单 GPU 使用。如果只有一个 GPU 可用,则可以忽略此命令。
此外,我们希望避免此笔记本在初始化并设置memory_growth为活动状态时实例化整个 GPU 内存。
备注:Sionna 不需要 GPU。所有内容也可以在 CPU 上运行 - 但您可能需要等待一段时间。
# Configure the notebook to use only a single GPU and allocate only as much memory as needed
# For more details, see https://www.tensorflow.org/guide/gpu
gpus = tf.config.list_physical_devices('GPU')
print('Number of GPUs available :', len(gpus))
if gpus:gpu_num = 0 # Index of the GPU to be usedtry:#tf.config.set_visible_devices([], 'GPU')tf.config.set_visible_devices(gpus[gpu_num], 'GPU')print('Only GPU number', gpu_num, 'used.')tf.config.experimental.set_memory_growth(gpus[gpu_num], True)except RuntimeError as e:print(e)
Sionna 数据流和设计范式
Sionna 本质上通过批处理实现模拟并行化,即批处理维度中的每个元素都是独立模拟的。
这意味着第一个张量维度始终用于帧间并行化,类似于Matlab/NumPy 模拟中的外部for 循环。
为了保持数据流高效,Sionna 遵循一些简单的设计原则:
信号处理组件作为单独的Keras 层实现。
tf.float32分别用作首选数据类型和tf.complex64复值数据类型。这允许更简单地重复使用组件(例如,相同的置乱层可用于二进制输入和 LLR 值)。
模型可以在急切模式下开发,从而允许简单(且快速)地修改系统参数。
可以在更快的图形模式下执行数字运算模拟,甚至可以为大多数组件提供XLA加速。
只要有可能,组件就会通过自动分级自动区分,以简化深度学习设计流程。
代码被构建为用于不同任务的子包,例如通道编码、映射等(有关详细信息,请参阅API 文档)。
划分为单独的块简化了部署,并且所有层和功能都附带单元测试以确保其正确的行为。
这些范例简化了我们的组件在广泛的通信相关应用程序中的可重用性和可靠性。
关于随机数生成的注释
加载 Sionna 时,它会实例化Python、NumPy和TensorFlow的随机数生成器 (RNG) 。您可以选择设置一个种子,只要只使用这些 RNG,它就会使所有结果都具有确定性。在下面的单元格中,您可以看到如何设置此种子以及如何使用不同的 RNG。
sionna.config.seed = 40# Python RNG - use instead of
# import random
# random.randint(0, 10)
print(sionna.config.py_rng.randint(0,10))# NumPy RNG - use instead of
# import numpy as np
# np.random.randint(0, 10)
print(sionna.config.np_rng.integers(0,10))# TensorFlow RNG - use instead of
# import tensorflow as tf
# tf.random.uniform(shape=[1], minval=0, maxval=10, dtype=tf.int32)
print(sionna.config.tf_rng.uniform(shape=[1], minval=0, maxval=10, dtype=tf.int32))
7
5
tf.Tensor([2],形状=(1,),dtype=int32)
让我们开始吧 - 第一层(Eager 模式)
每一层都需要初始化一次才能使用。
提示:使用API 文档查找所有现有组件的概述。
我们现在想通过 AWGN 信道传输一些符号。首先,我们需要初始化相应的层。
channel = sionna.channel.AWGN() # init AWGN channel layer
在第一个例子中,我们想要将高斯噪声添加到某些给定值中x。
记住 - 第一个维度是批次维度。
我们模拟 2 个消息帧,每个帧包含 4 个符号。
备注:AWGN信道定义为复值。
# define a (complex-valued) tensor to be transmitted
x = tf.constant([[0., 1.5, 1., 0.],[-1., 0., -2, 3 ]], dtype=tf.complex64)# let's have look at the shape
print("Shape of x: ", x.shape)
print("Values of x: ", x)
x 的形状:(2,4)
x 的值:tf.Tensor(
[[ 0.+0.j 1.5+0.j 1.+0.j 0.+0.j][-1. +0.j 0. +0.j -2. +0.j 3. +0.j]], shape=(2, 4), dtype=complex64)
我们希望模拟信噪比为 5 dB 的信道。为此,我们可以简单地调用之前定义的层channel。
如果您从未使用过Keras,那么您可以将层视为一个函数:它有一个输入并返回处理后的输出。
备注:每次执行此单元时都会绘制一个新的噪声实现。
ebno_db = 5# calculate noise variance from given EbNo
no = sionna.utils.ebnodb2no(ebno_db = ebno_db,num_bits_per_symbol=2, # QPSKcoderate=1)
y = channel([x, no])print("Noisy symbols are: ", y)
噪声符号为:tf.Tensor(
[[-0.02095131+0.19480924j 1.3121496 +0.05868753j 0.67274046-0.13089974j-0.303877 +0.19657521j][-0.9967893 +0.12435442j -0.5632028 -0.14088595j -1.9711018 -0.3130482j2.7371373 +0.26847288j]],形状=(2,4),dtype=complex64)
批次和多维张量
Sionna 原生支持多维张量。
大多数层在最后一个维度上运行,并且可以具有任意输入形状(在输出时保留)。
假设我们想为长度为 500 的 64 个代码字添加 CRC-24 校验(例如,每个子载波不同的 CRC)。此外,我们想在一批 100 个样本上并行化模拟。
batch_size = 100 # outer level of parallelism
num_codewords = 64 # codewords per batch sample
info_bit_length = 500 # info bits PER codewordsource = sionna.utils.BinarySource() # yields random bitsu = source([batch_size, num_codewords, info_bit_length]) # call the source layer
print("Shape of u: ", u.shape)# initialize an CRC encoder with the standard compliant "CRC24A" polynomial
encoder_crc = sionna.fec.crc.CRCEncoder("CRC24A")
decoder_crc = sionna.fec.crc.CRCDecoder(encoder_crc) # connect to encoder# add the CRC to the information bits u
c = encoder_crc(u) # returns a list [c, crc_valid]
print("Shape of c: ", c.shape)
print("Processed bits: ", np.size(c.numpy()))# we can also verify the results
# returns list of [info bits without CRC bits, indicator if CRC holds]
u_hat, crc_valid = decoder_crc(c)
print("Shape of u_hat: ", u_hat.shape)
print("Shape of crc_valid: ", crc_valid.shape)print("Valid CRC check of first codeword: ", crc_valid.numpy()[0,0,0])
u 的形状:(100,64,500)
c 的形状:(100,64,524)
已处理位数:3353600
u_hat 的形状:(100、64、500)
crc_valid 的形状:(100,64,1)
第一个代码字的有效 CRC 校验:真
我们想做另一个模拟,但针对 5 个独立用户。
我们不需要定义 5 个不同的张量,只需添加另一个维度即可。
num_users = 5u = source([batch_size, num_users, num_codewords, info_bit_length])
print("New shape of u: ", u.shape)# We can re-use the same encoder as before
c = encoder_crc(u)
print("New shape of c: ", c.shape)
print("Processed bits: ", np.size(c.numpy()))
u 的新形状:(100, 5, 64, 500)
c 的新形状:(100,5,64,524)
已处理位数:16768000
良好的结果可视化往往有助于获得新的研究思路。因此,Sionna 具有内置的绘图功能。
让我们看一下16-QAM星座。
constellation = sionna.mapping.Constellation("qam", num_bits_per_symbol=4)
constellation.show();
首次链路级仿真
我们已经可以用几个简单的命令来构建强大的代码。
如前所述,Sionna 旨在将系统复杂性隐藏到 Keras 层中。但是,我们仍然希望提供尽可能多的灵活性。因此,大多数层都有几种初始参数选择,但通常默认选择是一个好的开始。
提示:API 文档提供了许多有用的参考和实现细节。
# system parameters
n_ldpc = 500 # LDPC codeword length
k_ldpc = 250 # number of info bits per LDPC codeword
coderate = k_ldpc / n_ldpc
num_bits_per_symbol = 4 # number of bits mapped to one symbol (cf. QAM)
通常,会实现几种不同的算法,例如,解映射器支持“true app”解映射,也支持“max-log”解映射。
LDPC BP译码器的校验节点(CN)更新功能也支持多种算法。
demapping_method = "app" # try "max-log"
ldpc_cn_type = "boxplus" # try also "minsum"
让我们根据给定的系统参数初始化所有必需的组件。
binary_source = sionna.utils.BinarySource()
encoder = sionna.fec.ldpc.encoding.LDPC5GEncoder(k_ldpc, n_ldpc)
constellation = sionna.mapping.Constellation("qam", num_bits_per_symbol)
mapper = sionna.mapping.Mapper(constellation=constellation)
channel = sionna.channel.AWGN()
demapper = sionna.mapping.Demapper(demapping_method,constellation=constellation)
decoder = sionna.fec.ldpc.decoding.LDPC5GDecoder(encoder,hard_out=True, cn_type=ldpc_cn_type,num_iter=20)
我们现在可以在Eager 模式下运行代码。这允许我们随时修改结构 - 您可以尝试不同的batch_size或不同的 SNR ebno_db。
# simulation parameters
batch_size = 1000
ebno_db = 4# Generate a batch of random bit vectors
b = binary_source([batch_size, k_ldpc])# Encode the bits using 5G LDPC code
print("Shape before encoding: ", b.shape)
c = encoder(b)
print("Shape after encoding: ", c.shape)# Map bits to constellation symbols
x = mapper(c)
print("Shape after mapping: ", x.shape)# Transmit over an AWGN channel at SNR 'ebno_db'
no = sionna.utils.ebnodb2no(ebno_db, num_bits_per_symbol, coderate)
y = channel([x, no])
print("Shape after channel: ", y.shape)# Demap to LLRs
llr = demapper([y, no])
print("Shape after demapping: ", llr.shape)# LDPC decoding using 20 BP iterations
b_hat = decoder(llr)
print("Shape after decoding: ", b_hat.shape)# calculate BERs
c_hat = tf.cast(tf.less(0.0, llr), tf.float32) # hard-decided bits before dec.
ber_uncoded = sionna.utils.metrics.compute_ber(c, c_hat)ber_coded = sionna.utils.metrics.compute_ber(b, b_hat)print("BER uncoded = {:.3f} at EbNo = {:.1f} dB".format(ber_uncoded, ebno_db))
print("BER after decoding = {:.3f} at EbNo = {:.1f} dB".format(ber_coded, ebno_db))
print("In total {} bits were simulated".format(np.size(b.numpy())))
编码前形状:(1000,250)
编码后的形状:(1000,500)
映射后的形状:(1000,125)
通道后的形状:(1000,125)
解映射后的形状:(1000,500)
解码后形状:(1000,250)
未编码 BER = 0.119,EbNo = 4.0 dB
解码后 BER = 0.008,EbNo = 4.0 dB
总共模拟了 250000 位
总结一下:我们模拟了 250,000 比特的传输,包括高阶调制和信道编码!
但是我们可以通过TF 图形执行甚至更快地完成任务!
建立端到端模型
我们现在定义一个更方便训练和蒙特卡洛模拟的Keras 模型。
我们模拟了时变多径信道( 3GPP TR38.901 中的TDL-A模型)上的传输。为此,我们使用了 OFDM 和具有高阶调制的传统比特交织编码调制 (BICM) 方案。信息比特受 5G 兼容 LDPC 码保护。
备注:由于参数数量较多,我们将其定义为字典。
class e2e_model(tf.keras.Model): # inherits from keras.model"""Example model for end-to-end link-level simulations.Parameters----------params: dictA dictionary defining the system parameters.Input-----batch_size: int or tf.intThe batch_sizeused for the simulation.ebno_db: float or tf.floatA float defining the simulation SNR.Output------(b, b_hat):Tuple:b: tf.float32A tensor of shape `[batch_size, k]` containing the transmittedinformation bits.b_hat: tf.float32A tensor of shape `[batch_size, k]` containing the receiver'sestimate of the transmitted information bits."""def __init__(self,params):super().__init__()# Define an OFDM Resource Grid Objectself.rg = sionna.ofdm.ResourceGrid(num_ofdm_symbols=params["num_ofdm_symbols"],fft_size=params["fft_size"],subcarrier_spacing=params["subcarrier_spacing"],num_tx=1,num_streams_per_tx=1,cyclic_prefix_length=params["cyclic_prefix_length"],pilot_pattern="kronecker",pilot_ofdm_symbol_indices=params["pilot_ofdm_symbol_indices"])# Create a Stream Management objectself.sm = sionna.mimo.StreamManagement(rx_tx_association=np.array([[1]]),num_streams_per_tx=1)self.coderate = params["coderate"]self.num_bits_per_symbol = params["num_bits_per_symbol"]self.n = int(self.rg.num_data_symbols*self.num_bits_per_symbol)self.k = int(self.n*coderate)# Init layersself.binary_source = sionna.utils.BinarySource()self.encoder = sionna.fec.ldpc.encoding.LDPC5GEncoder(self.k, self.n)self.interleaver = sionna.fec.interleaving.RowColumnInterleaver(row_depth=self.num_bits_per_symbol)self.deinterleaver = sionna.fec.interleaving.Deinterleaver(self.interleaver)self.mapper = sionna.mapping.Mapper("qam", self.num_bits_per_symbol)self.rg_mapper = sionna.ofdm.ResourceGridMapper(self.rg)self.tdl = sionna.channel.tr38901.TDL(model="A",delay_spread=params["delay_spread"],carrier_frequency=params["carrier_frequency"],min_speed=params["min_speed"],max_speed=params["max_speed"])self.channel = sionna.channel.OFDMChannel(self.tdl, self.rg, add_awgn=True, normalize_channel=True)self.ls_est = sionna.ofdm.LSChannelEstimator(self.rg, interpolation_type="nn")self.lmmse_equ = sionna.ofdm.LMMSEEqualizer(self.rg, self.sm)self.demapper = sionna.mapping.Demapper(params["demapping_method"],"qam", self.num_bits_per_symbol)self.decoder = sionna.fec.ldpc.decoding.LDPC5GDecoder(self.encoder,hard_out=True,cn_type=params["cn_type"],num_iter=params["bp_iter"])print("Number of pilots: {}".format(self.rg.num_pilot_symbols))print("Number of data symbols: {}".format(self.rg.num_data_symbols))print("Number of resource elements: {}".format(self.rg.num_resource_elements))print("Pilot overhead: {:.2f}%".format(self.rg.num_pilot_symbols /self.rg.num_resource_elements*100))print("Cyclic prefix overhead: {:.2f}%".format(params["cyclic_prefix_length"] /(params["cyclic_prefix_length"]+params["fft_size"])*100))print("Each frame contains {} information bits".format(self.k))def call(self, batch_size, ebno_db):# Generate a batch of random bit vectors# We need two dummy dimension representing the number of# transmitters and streams per transmitter, respectively.b = self.binary_source([batch_size, 1, 1, self.k])# Encode the bits using the all-zero dummy encoderc = self.encoder(b)# Interleave the bits before mapping (BICM)c_int = self.interleaver(c)# Map bits to constellation symbolss = self.mapper(c_int)# Map symbols onto OFDM ressource gridx_rg = self.rg_mapper(s)# Transmit over noisy multi-path channelno = sionna.utils.ebnodb2no(ebno_db, self.num_bits_per_symbol, self.coderate, self.rg)y = self.channel([x_rg, no])# LS Channel estimation with nearest pilot interpolationh_hat, err_var = self.ls_est ([y, no])# LMMSE Equalizationx_hat, no_eff = self.lmmse_equ([y, h_hat, err_var, no])# Demap to LLRsllr = self.demapper([x_hat, no_eff])# Deinterleave before decodingllr_int = self.deinterleaver(llr)# Decodeb_hat = self.decoder(llr_int)# number of simulated bitsnb_bits = batch_size*self.k# transmitted bits and the receiver's estimate after decodingreturn b, b_hat
让我们将模拟的系统参数定义为字典:
sys_params = {# Channel"carrier_frequency" : 3.5e9,"delay_spread" : 100e-9,"min_speed" : 3,"max_speed" : 3,"tdl_model" : "A",# OFDM"fft_size" : 256,"subcarrier_spacing" : 30e3,"num_ofdm_symbols" : 14,"cyclic_prefix_length" : 16,"pilot_ofdm_symbol_indices" : [2, 11],# Code & Modulation"coderate" : 0.5,"num_bits_per_symbol" : 4,"demapping_method" : "app","cn_type" : "boxplus","bp_iter" : 20
}
…并初始化模型:
model = e2e_model(sys_params)
飞行员人数:512
数据符号数:3072
资源元素数量:3584
飞行员开销:14.29%
循环前缀开销:5.88%
每帧包含6144个信息位
与以前一样,我们可以简单地调用模型来模拟给定模拟参数的 BER。
#simulation parameters
ebno_db = 10
batch_size = 200# and call the model
b, b_hat = model(batch_size, ebno_db)ber = sionna.utils.metrics.compute_ber(b, b_hat)
nb_bits = np.size(b.numpy())print("BER: {:.4} at Eb/No of {} dB and {} simulated bits".format(ber.numpy(), ebno_db, nb_bits))
BER:Eb/No 为 10 dB 且模拟比特数为 1228800 时为 0.001245
运行一些吞吐量测试(图形模式)
Sionna 不仅是一个易于使用的库,而且速度非常快。让我们测量一下上面定义的模型的吞吐量。
我们比较了eager和graph执行模式(详情请参阅Tensorflow 文档),以及XLA 的 eager(请参阅https://www.tensorflow.org/xla#enable_xla_for_tensorflow_models)。请注意,我们需要激活sionna.config.xla_compat功能才能使 XLA 正常工作。
提示:更改batch_size以查看批处理并行性如何提高吞吐量。根据您的机器,batch_size可能太大。
import time # this block requires the timeit librarybatch_size = 200
ebno_db = 5 # evalaute SNR point
repetitions = 4 # throughput is averaged over multiple runsdef get_throughput(batch_size, ebno_db, model, repetitions=1):""" Simulate throughput in bit/s per ebno_db point.The results are average over `repetition` trials.Input-----batch_size: int or tf.int32Batch-size for evaluation.ebno_db: float or tf.float32A tensor containing the SNR points be evaluatedmodel:Function or model that yields the transmitted bits `u` and thereceiver's estimate `u_hat` for a given ``batch_size`` and``ebno_db``.repetitions: intAn integer defining how many trails of the throughputsimulation are averaged."""# call model once to be sure it is compile properly# otherwise time to build graph is measured as well.u, u_hat = model(tf.constant(batch_size, tf.int32),tf.constant(ebno_db, tf.float32))t_start = time.perf_counter()# average over multiple runsfor _ in range(repetitions):u, u_hat = model(tf.constant(batch_size, tf.int32),tf.constant(ebno_db, tf. float32))t_stop = time.perf_counter()# throughput in bit/sthroughput = np.size(u.numpy())*repetitions / (t_stop - t_start)return throughput# eager mode - just call the model
def run_eager(batch_size, ebno_db):return model(batch_size, ebno_db)time_eager = get_throughput(batch_size, ebno_db, run_eager, repetitions=4)# the decorator "@tf.function" enables the graph mode
@tf.function
def run_graph(batch_size, ebno_db):return model(batch_size, ebno_db)time_graph = get_throughput(batch_size, ebno_db, run_graph, repetitions=4)# the decorator "@tf.function(jit_compile=True)" enables the graph mode with XLA
# we need to activate the sionna.config.xla_compat feature for this to work
sionna.config.xla_compat=True
@tf.function(jit_compile=True)
def run_graph_xla(batch_size, ebno_db):return model(batch_size, ebno_db)time_graph_xla = get_throughput(batch_size, ebno_db, run_graph_xla, repetitions=4)
# we deactivate the sionna.config.xla_compat so that the cell can be run mutiple times
sionna.config.xla_compat=Falseprint(f"Throughput in eager execution: {time_eager/1e6:.2f} Mb/s")
print(f"Throughput in graph execution: {time_graph/1e6:.2f} Mb/s")
print(f"Throughput in graph execution with XLA: {time_graph_xla/1e6:.2f} Mb/s")
XLA 可能导致数值精度降低。请谨慎使用。
Eager Execution 吞吐量:1.56 Mb/s
图形执行吞吐量:7.76 Mb/s
XLA 图形执行的吞吐量:57.66 Mb/s
显然,图形执行(使用 XLA)可产生更高的吞吐量(至少如果有快速 GPU 可用)。因此,对于详尽训练和蒙特卡罗模拟,图形模式(使用 XLA 和 GPU 加速)是首选。
误码率 (BER) 蒙特卡罗模拟
蒙特卡罗模拟在当今的通信研究和开发中无处不在。由于其高性能实现,Sionna 可直接用于模拟 BER,其性能可与编译语言相媲美 - 但仍然保持了脚本语言的灵活性。
ebno_dbs = np.arange(0, 15, 1.)
batch_size = 200 # reduce in case you receive an out-of-memory (OOM) errormax_mc_iter = 1000 # max number of Monte-Carlo iterations before going to next SNR point
num_target_block_errors = 500 # continue with next SNR point after target number of block errors# we use the built-in ber simulator function from Sionna which uses and early stop after reaching num_target_errors
sionna.config.xla_compat=True
ber_mc,_ = sionna.utils.sim_ber(run_graph_xla, # you can also evaluate the model directlyebno_dbs,batch_size=batch_size,num_target_block_errors=num_target_block_errors,max_mc_iter=max_mc_iter,verbose=True) # print status and summary
sionna.config.xla_compat=False
XLA 可能导致数值精度降低。请谨慎使用。
EbNo [dB] | BER | BLER | 比特错误 | 比特数 | 块错误 | 块数 | 运行时间 [s] | 状态
-------------------------------------------------------------------------------------------------------------------------------------------0.0 | 3.4352e-01 | 1.0000e+00 | 1266347 | 3686400 | 600 | 600 | 0.1 |达到目标块错误1.0 | 3.2095e-01 | 1.0000e+00 | 1183166 | 3686400 | 600 | 600 | 0.1 |达到目标块错误2.0 | 2.9738e-01 | 1.0000e+00 | 1096268 | 3686400 | 600 | 600 | 0.1 |达到目标块错误3.0 | 2.7369e-01 | 1.0000e+00 | 1008918 | 3686400 | 600 | 600 | 0.1 |达到目标块错误4.0 | 2.4703e-01 | 1.0000e+00 | 910649 | 3686400 | 600 | 600 | 0.1 |达到目标块错误5.0 | 2.1839e-01 | 1.0000e+00 | 805067 | 3686400 | 600 | 600 | 0.1 |达到目标块错误6.0 | 1.8570e-01 | 1.0000e+00 | 684560 | 3686400 | 600 | 600 | 0.1 |达到目标块错误7.0 | 1.1760e-01 | 9.9167e-01 | 433539 | 3686400 | 595 | 600 | 0.1 |达到目标块错误8.0 | 4.2571e-02 | 4.7833e-01 | 313870 | 7372800 | 574 | 1200 | 0.2 |达到目标块错误9.0 | 1.3462e-02 | 1.5206e-01 | 281220 | 20889600 | 517 | 3400 | 0.4 |达到目标块错误10.0 | 3.3929e-03 | 3.5352e-02 | 296011 | 87244800 | 502 | 14200 | 1.8 |达到目标块错误11.0 | 8.4720e-04 | 9.1758e-03 | 284203 | 335462400 | 501 | 54600 | 6.8 |达到目标块错误12.0 | 2.7327e-04 | 2.9002e-03 | 289455 | 1059225600 | 500 | 172400 | 21.3 |达到目标块错误13.0 | 8.8057e-05 | 8.9500e-04 | 108205 | 1228800000 | 179 | 200000 | 24.5 |已达到最大迭代次数14.0 | 3.4128e-05 | 3.3500e-04 | 41936 | 1228800000 | 67 | 200000 | 24.5 |已达到最大迭代次数
我们来看看结果。
sionna.utils.plotting.plot_ber(ebno_dbs,ber_mc,legend="E2E Model",ylabel="Coded BER");
结论
我们希望您对 Sionna 感到兴奋——还有更多的东西有待发现:
TensorBoard 调试可用
扩展到多 GPU 模拟很简单
请参阅可用的教程以获取更多高级示例。