├── .gitignore ├── LICENSE.txt ├── README.md ├── config ├── e2e_baseline.cfg ├── e2e_large.cfg ├── e2e_rt.cfg ├── nrx_large.cfg ├── nrx_large_64qam.cfg ├── nrx_large_qpsk.cfg ├── nrx_large_var_mcs.cfg ├── nrx_large_var_mcs_64qam_masking.cfg ├── nrx_rt.cfg ├── nrx_rt_64qam.cfg ├── nrx_rt_qpsk.cfg ├── nrx_rt_var_mcs.cfg ├── nrx_site_specific.cfg ├── nrx_site_specific_100k.cfg ├── nrx_site_specific_baseline.cfg ├── nrx_site_specific_baseline_large.cfg └── nrx_site_specific_large.cfg ├── misc ├── nrx_bler_results.png ├── nrx_latency.png └── nrx_overview.png ├── notebooks ├── e2e_pilotless_communications.ipynb ├── jumpstart_tutorial.ipynb ├── nrx_architecture.ipynb ├── plot_results.ipynb ├── real_time_nrx.ipynb ├── site_specific_neural_receivers.ipynb └── variable_mcs_nrx.ipynb ├── requirements.txt ├── results ├── e2e_baseline_results ├── e2e_large_results ├── e2e_rt_results ├── mixed_mcs_results ├── nrx_large_64qam_results ├── nrx_large_qpsk_results ├── nrx_large_results ├── nrx_large_sweep_results ├── nrx_large_var_mcs_64qam_masking_results ├── nrx_large_var_mcs_masking_sweep_results ├── nrx_large_var_mcs_results ├── nrx_rt_64qam_results ├── nrx_rt_qpsk_results ├── nrx_rt_results ├── nrx_rt_var_mcs_results ├── nrx_site_specific_100k_results ├── nrx_site_specific_baseline_large_results ├── nrx_site_specific_baseline_results ├── nrx_site_specific_large_results ├── nrx_site_specific_results └── nrx_site_specific_sweep_results ├── scripts ├── compute_cov_mat.py ├── evaluate.py ├── export_onnx.py └── train_neural_rx.py ├── utils ├── __init__.py ├── baseline_rx.py ├── channel_models.py ├── e2e_model.py ├── impairments.py ├── neural_rx.py ├── onnx_utils.py ├── parameters.py └── utils.py └── weights ├── e2e_baseline_weights ├── e2e_large_weights ├── e2e_rt_weights ├── nrx_large_64qam_weights ├── nrx_large_qpsk_weights ├── nrx_large_var_mcs_64qam_masking_weights ├── nrx_large_var_mcs_weights ├── nrx_large_weights ├── nrx_rt_64qam_weights ├── nrx_rt_var_mcs_weights ├── nrx_rt_weights ├── nrx_site_specific_100k_weights ├── nrx_site_specific_baseline_large_weights ├── nrx_site_specific_baseline_weights ├── nrx_site_specific_large_weights └── nrx_site_specific_weights /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | build/ 3 | __pycache__ 4 | .ipynb_checkpoints 5 | .pytest_cache 6 | .DS_Store 7 | .buildinfo 8 | .Trash-1000 9 | .logs 10 | dev 11 | _build 12 | logs/ 13 | dev/ 14 | **/*_cov_mat.npy 15 | onnx_models/* 16 | *.tfrecord 17 | /weights/site_specific_sweep/* 18 | *.csv 19 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | NVIDIA License 2 | 3 | 1. Definitions 4 | 5 | “Licensor” means any person or entity that distributes its Work. 6 | “Work” means (a) the original work of authorship made available under this license, which may include software, documentation, or other files, and (b) any additions to or derivative works thereof that are made available under this license. 7 | The terms “reproduce,” “reproduction,” “derivative works,” and “distribution” have the meaning as provided under U.S. copyright law; provided, however, that for the purposes of this license, derivative works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work. 8 | Works are “made available” under this license by including in or with the Work either (a) a copyright notice referencing the applicability of this license to the Work, or (b) a copy of this license. 9 | 10 | 2. License Grant 11 | 12 | 2.1 Copyright Grant. Subject to the terms and conditions of this license, each Licensor grants to you a perpetual, worldwide, non-exclusive, royalty-free, copyright license to use, reproduce, prepare derivative works of, publicly display, publicly perform, sublicense and distribute its Work and any resulting derivative works in any form. 13 | 14 | 3. Limitations 15 | 16 | 3.1 Redistribution. You may reproduce or distribute the Work only if (a) you do so under this license, (b) you include a complete copy of this license with your distribution, and (c) you retain without modification any copyright, patent, trademark, or attribution notices that are present in the Work. 17 | 18 | 3.2 Derivative Works. You may specify that additional or different terms apply to the use, reproduction, and distribution of your derivative works of the Work (“Your Terms”) only if (a) Your Terms provide that the use limitation in Section 3.3 applies to your derivative works, and (b) you identify the specific derivative works that are subject to Your Terms. Notwithstanding Your Terms, this license (including the redistribution requirements in Section 3.1) will continue to apply to the Work itself. 19 | 20 | 3.3 Use Limitation. The Work and any derivative works thereof only may be used or intended for use non-commercially. Notwithstanding the foregoing, NVIDIA Corporation and its affiliates may use the Work and any derivative works commercially. As used herein, “non-commercially” means for research or evaluation purposes only. 21 | 22 | 3.4 Patent Claims. If you bring or threaten to bring a patent claim against any Licensor (including any claim, cross-claim or counterclaim in a lawsuit) to enforce any patents that you allege are infringed by any Work, then your rights under this license from such Licensor (including the grant in Section 2.1) will terminate immediately. 23 | 24 | 3.5 Trademarks. This license does not grant any rights to use any Licensor’s or its affiliates’ names, logos, or trademarks, except as necessary to reproduce the notices described in this license. 25 | 26 | 3.6 Termination. If you violate any term of this license, then your rights under this license (including the grant in Section 2.1) will terminate immediately. 27 | 28 | 4. Disclaimer of Warranty. 29 | 30 | THE WORK IS PROVIDED “AS IS” WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WARRANTIES OR CONDITIONS OF 31 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT. YOU BEAR THE RISK OF UNDERTAKING ANY ACTIVITIES UNDER THIS LICENSE. 32 | 33 | 5. Limitation of Liability. 34 | 35 | EXCEPT AS PROHIBITED BY APPLICABLE LAW, IN NO EVENT AND UNDER NO LEGAL THEORY, WHETHER IN TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE SHALL ANY LICENSOR BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATED TO THIS LICENSE, THE USE OR INABILITY TO USE THE WORK (INCLUDING BUT NOT LIMITED TO LOSS OF GOODWILL, BUSINESS INTERRUPTION, LOST PROFITS OR DATA, COMPUTER FAILURE OR MALFUNCTION, OR ANY OTHER DAMAGES OR LOSSES), EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | # Real-Time Inference of 5G NR Multi-user MIMO Neural Receivers 12 | 13 | The code in this repository allows to design, train, and evaluate [neural 14 | receivers](https://developer.nvidia.com/blog/towards-environment-specific-base-stations-ai-ml-driven-neural-5g-nr-multi-user-mimo-receiver/) 15 | using the [NVIDIA® Sionna™ link-level simulation 16 | library](https://nvlabs.github.io/sionna/) and TensorFlow. Further, trained 17 | receivers can be prepared for real-time inference via [NVIDIA® 18 | TensorRT™](https://developer.nvidia.com/tensorrt). 19 | 20 | [

](notebooks/jumpstart_tutorial.ipynb) 21 | 22 | The following features are currently supported: 23 | 24 | - 5G NR compliant Multi-user MIMO PUSCH receiver 25 | - Training pipeline using 3GPP compliant channel models 26 | - TensorRT / ONNX model export for real-time inference 27 | - Support for varying number of PRBs, users, and different MCS schemes per user 28 | - End-to-end learning of custom constellations for [pilotless communications](https://arxiv.org/pdf/2009.05261) [3] 29 | - Site-specific training using ray-tracing based channel simulations from [SionnaRT](https://nvlabs.github.io/sionna/api/rt.html) as done in [2] 30 | 31 | We recommend starting with the [Jumpstart NRX Tutorial notebook](notebooks/jumpstart_tutorial.ipynb) for a detailed introduction and overview of the project. 32 | 33 | The basic neural receiver architecture is introduced and described in [a Neural Receiver for 5G NR Multi-user MIMO](https://arxiv.org/pdf/2312.02601) [1]. 34 | The real-time experiments and the site-specific training is described in [Design of a Standard-Compliant Real-Time 35 | Neural Receiver for 5G NR](TODO) [2]. 36 | 37 | Demos of this receiver architecture have been shown at [Mobile World Congress 2023](https://www.youtube.com/watch?v=BQyxBYzdg5k) and [Mobile World Congress 2024](https://www.keysight.com/us/en/assets/3124-1306/demos/6G-AI-Neural-Receiver-Design.mp4). 38 | 39 | For further details regarding solutions for deployment in an actual Radio Access Network (RAN), we recommend registering for the [NVIDIA 6G Developer Program](https://developer.nvidia.com/6g-program). 40 | 41 | [

](notebooks/jumpstart_tutorial.ipynb) 42 | 43 | ## Summary 44 | 45 | We introduce a neural network (NN)-based multi-user multiple-input 46 | multiple-output (MU-MIMO) receiver with full 5G New Radio (5G NR) physical 47 | uplink shared channel (PUSCH) compatibility based on graph and convolutional 48 | neural network (CGNN) components. The proposed architecture can be easily 49 | re-parametrized to an arbitrary number of sub-carriers and supports a varying 50 | number of users without the need for any additional re-training. The receiver 51 | operates on an entire 5G NR slot, i.e., it processes the entire received 52 | orthogonal frequency division multiplexing (OFDM) time-frequency resource grid 53 | by jointly performing channel estimation, equalization, and demapping. We show 54 | the importance of a carefully designed training process such that the trained 55 | receiver does not overfit to a specific channel realization and remains 56 | universal for a wide range of different unseen channel conditions. A particular 57 | focus of the architecture design is put on the real-time inference capability 58 | such that the receiver can be executed within 1 ms latency on an NVIDIA A100 59 | GPU. 60 | 61 | [

](notebooks/real_time_nrx.ipynb) 62 | 63 | ## Setup 64 | 65 | Running this code requires [Sionna 0.18](https://nvlabs.github.io/sionna/). 66 | To run the notebooks on your machine, you also need [Jupyter](https://jupyter.org). 67 | We recommend Ubuntu 22.04, Python 3.10, and TensorFlow 2.15. 68 | 69 | For [TensorRT](https://developer.nvidia.com/tensorrt), we recommend version 9.6 and newer. 70 | For [ONNX](https://onnx.ai/) exporting, the Python package `onnx==1.14` is required (`onnx==1.15` does not work due to a known bug). 71 | 72 | ## Structure of this repository 73 | 74 | This repository is structured in the following way: 75 | - [config](config/) contains the system configurations for different experiments 76 | - [notebooks](notebooks/) contains tutorials and code examples 77 | - [scripts](scripts/) contains the scripts to train, evaluate and debug the NRX 78 | - [utils](utils/) contains the NRX definition and all Python utilities 79 | - [weights](weights/) contains weights of pre-trained neural receivers for different configuration files 80 | - [results](results/) contains pre-computed BLER performance results 81 | 82 | The following two folders will be generated locally: 83 | - `logs` contains log files of the training 84 | - `onnx_models` contains exported ONNX neural receiver models 85 | - `data` contains a ray tracing-based dataset of channel realizations for site-specific evaluation 86 | 87 | We recommend starting with the [Jumpstart NRX Tutorial notebook](notebooks/jumpstart_tutorial.ipynb) for a detailed introduction and overview of the project. 88 | 89 | ## References 90 | 91 | [1] S. Cammerer, F. Aït Aoudia, J. Hoydis, A. Oeldemann, A. Roessler, T. Mayer, and A. Keller, "[A Neural Receiver for 5G NR Multi-user MIMO](https://arxiv.org/pdf/2312.02601)", IEEE Workshops (GC Wkshps), Dec. 2023. 92 | 93 | [2] R. Wiesmayr, S, Cammerer, F. Aït Aoudia, J. Hoydis, J. Zakrzewski, and Alexander Keller, "[Design of a Standard-Compliant Real-Time Neural Receiver for 5G NR](https://arxiv.org/abs/2409.02912)", arxiv preprint, 2024. 94 | 95 | [3] F. Aït Aoudia and J. Hoydis, "[End-to-end Learning for OFDM: From Neural Receivers to Pilotless Communication](https://arxiv.org/pdf/2009.05261)", IEEE Trans on Wireless Commun., 2021. 96 | 97 | ## License 98 | 99 | Copyright © 2024, NVIDIA Corporation. All rights reserved. 100 | 101 | This work is made available under the [NVIDIA License](LICENSE.txt). 102 | 103 | # Citation 104 | 105 | ``` 106 | @software{neural_rx, 107 | title = {Real-time 5G NR Multi-user MIMO Receivers}, 108 | author = {Sebastian Cammerer, Reinhard Wiesmayr, Fayçal Aït Aoudia, Jakob Hoydis, Tommi Koivisto, Jakub Zakrzewski, Ruqing Xu, Pawel Morkisz, Chris Dick, and Alexander Keller}, 109 | note = {https://github.com/NVlabs/neural-rx}, 110 | year = 2024 111 | } 112 | ``` 113 | 114 | # Acknowledgement 115 | 116 | This work has received financial support from the European Union under 117 | Grant Agreement 101096379 (CENTRIC). Views and opinions expressed are 118 | however those of the author(s) only and do not necessarily reflect those of the 119 | European Union or the European Commission (granting authority). Neither the 120 | European Union nor the granting authority can be held responsible for them. 121 | -------------------------------------------------------------------------------- /config/e2e_baseline.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'e2e_baseline' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted LLRs 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 1 39 | verbose = False 40 | dmrs_port_sets = [[0]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be a multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # each entry of the training schedule denotes 78 | # [number of SGD iterations, learning rate, batch_size, trainable_const.] 79 | # second part finetunes the neural receiver for a fixed constellation 80 | training_schedule = { 81 | "num_iter": [1e6, 7e6], # baseline is not trained 82 | "learning_rate": [0.001, 0.001], 83 | "batch_size": [128, 128], 84 | "train_tx": [False, False], 85 | "min_training_snr_db": [[0., 0.],[0., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "max_training_snr_db": [[10., 15.],[5., 7.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "double_readout": [True, True], # use additional MSE loss on h_hat 88 | "apply_multiloss": [False, False], 89 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 90 | 91 | num_iter_train_save = 1000 92 | max_ut_velocity = 56. 93 | min_ut_velocity = 0. 94 | channel_norm = False 95 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 96 | # UMi 97 | channel_type = 'UMi' 98 | eval_ebno_db_arr = [4.0] 99 | # TDL (for two UEs) 100 | #channel_type = 'DoubleTDLlow' 101 | #min_training_snr_db = [-4., -4.] 102 | #max_training_snr_db = [8., 8.] 103 | xla = True # Activate XLA for the training loop 104 | tfrecord_filename = "na" # only relevant if training is done with a dataset 105 | 106 | [evaluation] 107 | # the following parameters are used during evaluation 108 | snr_db_eval_min = -2 109 | snr_db_eval_max = 7 110 | snr_db_eval_stepsize = 1 111 | max_ut_velocity_eval = 56 112 | min_ut_velocity_eval = 56 113 | cfo_offset_ppm_eval = 0.0 114 | tfrecord_filename_eval = "na" 115 | channel_type_eval = 'TDL-B100' # 1 User 116 | channel_norm_eval = False 117 | n_size_bwp_eval = 132 118 | batch_size_eval = 30 119 | batch_size_eval_small = 3 # for k-best-based baselines 120 | -------------------------------------------------------------------------------- /config/e2e_large.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'e2e_large' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | # DMRS config such that it minimizes the number of DMRS positions 32 | # we mask the remaining few DMRS positions at the receiver input 33 | # Alternatively, one could write a custom PUSCHTransmitter to entireley remove 34 | # any DMRS. 35 | dmrs_mapping_type = "A" 36 | dmrs_config_type = 2 37 | dmrs_type_a_position = 2 38 | dmrs_additional_position = 0 39 | dmrs_length = 1 40 | dmrs_nid = [[1, 1], [1, 1]] 41 | n_scid = 1 42 | num_cdm_groups_without_data = 1 43 | verbose = False 44 | dmrs_port_sets = [[0]] 45 | n_rntis = [1, 1] 46 | n_ids = [1, 1] 47 | 48 | [baseline] 49 | demapping_type = 'maxlog' 50 | num_bp_iter = 20 51 | cn_type = 'boxplus' 52 | # For large num_prbs (>100), a low reduced complexity 53 | # LMMSE estimator is used where LMMSE is only performed over the 54 | # lmmse_num_prbs PRBS 55 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 56 | 57 | [neural_receiver] 58 | num_nrx_iter = 8 # defines number of cgnn_it stages 59 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 60 | d_s = 64 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 61 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 62 | # no aggregation due to single user 63 | num_units_agg = [[128], [128], [128], [128],[128], [128], [128], [128]] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 65 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 66 | max_num_tx = 1 # e2e only works for 1 transmitter 67 | min_num_tx = 1 68 | initial_chest = None # no DMRS available 69 | custom_constellation = True # activates trainable transmitter 70 | mask_pilots = True # mask DMRS positions for e2e experiments 71 | 72 | # quantization and other custom layer types 73 | layer_type_dense = "dense" 74 | layer_type_conv = "sepconv" # or "conv" 75 | layer_type_readout = "dense" 76 | nrx_dtype = tf.float32 77 | 78 | [training] 79 | # each entry of the training schedule denotes 80 | # [number of SGD iterations, learning rate, batch_size, trainable_const.] 81 | # second part finetunes the neural receiver for a fixed constellation 82 | training_schedule = { 83 | "num_iter": [3e5, 9e6], 84 | "learning_rate": [0.001, 0.001], 85 | "batch_size": [128, 128], 86 | "train_tx": [True, False], 87 | "min_training_snr_db": [[3.5], [1.]], # only 1 UE, is Eb/No [dB] if ebno==True 88 | "max_training_snr_db": [[3.5], [7.]], # only 1 UE, is Eb/No [dB] if ebno==True 89 | "double_readout": [True, True], # use additional MSE loss on h_hat 90 | "apply_multiloss": [True, True], 91 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 92 | # Remark: training SNR for custom const should be close to waterfall region 93 | 94 | num_iter_train_save = 1000 95 | max_ut_velocity = 56. 96 | min_ut_velocity = 0. 97 | channel_norm = False 98 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 99 | # TDL 100 | channel_type = 'TDL-C300' # hint: it may be beneficial to also train the receiver partially on UMi channels (after tx is already converged) 101 | eval_ebno_db_arr = [4.0] 102 | xla = True # Activate XLA for the training loop 103 | tfrecord_filename = "na" # only relevant if training is done with a dataset 104 | 105 | [evaluation] 106 | # the following parameters are used during evaluation 107 | snr_db_eval_min = -2 108 | snr_db_eval_max = 7 109 | snr_db_eval_stepsize = 1 110 | max_ut_velocity_eval = 56 111 | min_ut_velocity_eval = 56 112 | cfo_offset_ppm_eval = 0.0 113 | tfrecord_filename_eval = "na" 114 | channel_type_eval = 'TDL-B100' # 1 User 115 | channel_norm_eval = False 116 | n_size_bwp_eval = 132 117 | batch_size_eval = 20 118 | batch_size_eval_small = 2 # for kbest 119 | -------------------------------------------------------------------------------- /config/e2e_rt.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'e2e_rt' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | # DMRS config such that it minimizes the number of DMRS positions 32 | # we mask the remaining few DMRS positions at the receiver input 33 | # Alternatively, one could write a custom PUSCHTransmitter to entireley remove 34 | # any DMRS. 35 | dmrs_mapping_type = "A" 36 | dmrs_config_type = 2 37 | dmrs_type_a_position = 2 38 | dmrs_additional_position = 0 39 | dmrs_length = 1 40 | dmrs_nid = [[1, 1], [1, 1]] 41 | n_scid = 1 42 | num_cdm_groups_without_data = 1 43 | verbose = False 44 | dmrs_port_sets = [[0]] 45 | n_rntis = [1, 1] 46 | n_ids = [1, 1] 47 | 48 | [baseline] 49 | demapping_type = 'maxlog' 50 | num_bp_iter = 20 51 | cn_type = 'boxplus' 52 | # For large num_prbs (>100), a low reduced complexity 53 | # LMMSE estimator is used where LMMSE is only performed over the 54 | # lmmse_num_prbs PRBS 55 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 56 | 57 | [neural_receiver] 58 | num_nrx_iter = 4 # defines number of cgnn_it stages 59 | num_nrx_iter_eval = 4 # iterations used for evaluation; must be <= num_nrx_iter 60 | d_s = 64 # feature space dimensions 61 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 62 | num_units_agg = [[128],[128],[128],[128]] # number of neurons of state aggregation MLP (each list entry defines one layer) 63 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 64 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 65 | max_num_tx = 1 # e2e only works for 1 transmitter 66 | min_num_tx = 1 67 | initial_chest = None # no DMRS available 68 | custom_constellation = True # activates trainable transmitter 69 | mask_pilots = True # mask DMRS positions for e2e experiments 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | training_schedule = { 79 | "num_iter": [3e5, 9e6], 80 | "learning_rate": [0.001, 0.001], 81 | "batch_size": [128, 128], 82 | "train_tx": [True, False], 83 | "min_training_snr_db": [[3.5], [0.]], # only 1 UE, is Eb/No [dB] if ebno==True 84 | "max_training_snr_db": [[3.5], [7.]], # only 1 UE, is Eb/No [dB] if ebno==True 85 | "double_readout": [True, True], # use additional MSE loss on h_hat 86 | "apply_multiloss": [False, False], 87 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 88 | # Remark: training SNR for custom const should be close to waterfall region 89 | 90 | num_iter_train_save = 1000 91 | max_ut_velocity = 56. 92 | min_ut_velocity = 0. 93 | channel_norm = False 94 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 95 | # TDL 96 | channel_type = 'TDL-C300' # hint: it may be beneficial to also train the receiver parts on UMi channels (after tx is already converged) 97 | eval_ebno_db_arr = [4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 98 | 99 | xla = True # Activate XLA for the training loop 100 | tfrecord_filename = "na" # only relevant if training is done with a dataset 101 | 102 | [evaluation] 103 | # the following parameters are used during evaluation 104 | snr_db_eval_min = -2 105 | snr_db_eval_max = 7 106 | snr_db_eval_stepsize = 1 107 | max_ut_velocity_eval = 56 108 | min_ut_velocity_eval = 56 109 | cfo_offset_ppm_eval = 0.0 110 | tfrecord_filename_eval = "na" 111 | channel_type_eval = 'TDL-B100' # 1 User 112 | channel_norm_eval = False 113 | n_size_bwp_eval = 132 114 | batch_size_eval = 20 115 | batch_size_eval_small = 2 # for kbest 116 | -------------------------------------------------------------------------------- /config/nrx_large.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_large' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heurisitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # each entry of the training schedule denotes 78 | # [number of SGD iterations, learning rate, batch_size, trainable_const.] 79 | training_schedule = { 80 | "num_iter": [1e6, 9e6], 81 | "learning_rate": [0.001, 0.001], 82 | "batch_size": [128, 128], 83 | "train_tx": [False, False], 84 | "min_training_snr_db": [[0., 0.],[0., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 85 | "max_training_snr_db": [[10., 15.],[10., 12.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "double_readout": [True, True], # use additional MSE loss on h_hat 87 | "apply_multiloss": [True, True], 88 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 89 | 90 | num_iter_train_save = 1000 91 | max_ut_velocity = 56. 92 | min_ut_velocity = 0. 93 | channel_norm = False 94 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 95 | # UMi 96 | channel_type = 'UMi' 97 | eval_ebno_db_arr = [4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 98 | # TDL (for two UEs) 99 | #channel_type = 'DoubleTDLlow' 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "na" # only relevant if training is done with a dataset 102 | 103 | [evaluation] 104 | # the following parameters are used during evaluation 105 | snr_db_eval_min = -2 106 | snr_db_eval_max = 7 107 | snr_db_eval_stepsize = 1 108 | max_ut_velocity_eval = 56 109 | min_ut_velocity_eval = 56 110 | cfo_offset_ppm_eval = 0.0 111 | tfrecord_filename_eval = "na" 112 | channel_type_eval = "DoubleTDLlow" # 2 Users 113 | #channel_type_eval = 'TDL-B100' # 1 User 114 | channel_norm_eval = False 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | -------------------------------------------------------------------------------- /config/nrx_large_64qam.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_large_64qam' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [19] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heurisitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # each entry of the training schedule denotes 78 | # [number of SGD iterations, learning rate, batch_size, trainable_const.] 79 | training_schedule = { 80 | "num_iter": [1e6, 9e6], 81 | "learning_rate": [0.001, 0.001], 82 | "batch_size": [128, 128], 83 | "train_tx": [False, False], 84 | "min_training_snr_db": [[0., 0.],[2., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 85 | "max_training_snr_db": [[15., 20.],[15., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "double_readout": [True, True], # use additional MSE loss on h_hat 87 | "apply_multiloss": [True, True], 88 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 89 | 90 | num_iter_train_save = 1000 91 | max_ut_velocity = 56. 92 | min_ut_velocity = 0. 93 | channel_norm = False 94 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 95 | # UMi 96 | channel_type = 'UMi' 97 | eval_ebno_db_arr = [4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 98 | # TDL (for two UEs) 99 | #channel_type = 'DoubleTDLlow' 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "na" # only relevant if training is done with a dataset 102 | 103 | [evaluation] 104 | # the following parameters are used during evaluation 105 | snr_db_eval_min = -2 106 | snr_db_eval_max = 10 107 | snr_db_eval_stepsize = 1 108 | max_ut_velocity_eval = 56 109 | min_ut_velocity_eval = 56 110 | cfo_offset_ppm_eval = 0.0 111 | tfrecord_filename_eval = "na" 112 | channel_type_eval = "DoubleTDLlow" # 2 Users 113 | #channel_type_eval = 'TDL-B100' # 1 User 114 | channel_norm_eval = False 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | -------------------------------------------------------------------------------- /config/nrx_large_qpsk.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_large_qpsk' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [9] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heurisitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # each entry of the training schedule denotes 78 | # [number of SGD iterations, learning rate, batch_size, trainable_const.] 79 | training_schedule = { 80 | "num_iter": [1e6, 9e6], 81 | "learning_rate": [0.001, 0.001], 82 | "batch_size": [128, 128], 83 | "train_tx": [False, False], 84 | "min_training_snr_db": [[-2., 0.],[-1., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 85 | "max_training_snr_db": [[10., 10.],[8., 10.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "double_readout": [True, True], # use additional MSE loss on h_hat 87 | "apply_multiloss": [True, True], 88 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 89 | 90 | num_iter_train_save = 1000 91 | max_ut_velocity = 56. 92 | min_ut_velocity = 0. 93 | channel_norm = False 94 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 95 | # UMi 96 | channel_type = 'UMi' 97 | eval_ebno_db_arr = [1.0] # EbNo to evaluate model for each MCS during training every 1k iterations 98 | # TDL (for two UEs) 99 | #channel_type = 'DoubleTDLlow' 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "na" # only relevant if training is done with a dataset 102 | 103 | [evaluation] 104 | # the following parameters are used during evaluation 105 | snr_db_eval_min = -3 106 | snr_db_eval_max = 8 107 | snr_db_eval_stepsize = 1 108 | max_ut_velocity_eval = 56 109 | min_ut_velocity_eval = 56 110 | cfo_offset_ppm_eval = 0.0 111 | tfrecord_filename_eval = "na" 112 | channel_type_eval = "DoubleTDLlow" # 2 Users 113 | #channel_type_eval = 'TDL-B100' # 1 User 114 | channel_norm_eval = False 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | -------------------------------------------------------------------------------- /config/nrx_large_var_mcs.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_large_var_mcs' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [9, 14] # QPSK and 16-QAM 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # Each parameter in the training schedule is defined as list 78 | # The training loops over these parameters, i.e., performs num_iter[i] 79 | # SGD iterations for the ith set of parameters 80 | training_schedule = { 81 | "num_iter": [1e5, 5e6, 4e6], 82 | "learning_rate": [0.0005, 0.001, 0.0005], 83 | "batch_size": [128, 128, 128], 84 | "train_tx": [False, False, False], 85 | "min_training_snr_db": [[-2., -2.], [-2., -2.], [-1., -1.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "max_training_snr_db": [[8., 10.], [6., 8.], [4., 6.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "double_readout": [True, True, True], # use additional MSE loss on h_hat 88 | "weighting_double_readout": [0.02, 0.01, 0.005], # weighting between MSE & BCE loss 89 | "apply_multiloss": [True, True, True]} 90 | 91 | mcs_training_snr_db_offset = [[0.0, 4.0], [0.0, 2.0]] # first list is for 1 UE, second for 2 UEs 92 | 93 | num_iter_train_save = 1000 94 | max_ut_velocity = 56. 95 | min_ut_velocity = 0. 96 | channel_norm = False 97 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 98 | # UMi 99 | channel_type = 'UMi' 100 | eval_ebno_db_arr = [1.0, 4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 101 | # TDL (for two UEs) 102 | #channel_type = 'DoubleTDLlow' 103 | xla = True # Activate XLA for the training loop 104 | tfrecord_filename = "na" # only relevant if training is done with a dataset 105 | double_readout = True 106 | 107 | [evaluation] 108 | # the following parameters are used during evaluation 109 | snr_db_eval_min = -3 110 | snr_db_eval_max = 8 111 | snr_db_eval_stepsize = 1 112 | max_ut_velocity_eval = 56 # 400 Hz Doppler spread, which is assumed by TDL 400 channel model 113 | min_ut_velocity_eval = 56 114 | cfo_offset_ppm_eval = 0.0 115 | tfrecord_filename_eval = "na" 116 | channel_type_eval = "DoubleTDLlow" # 2 Users 117 | #channel_type_eval = 'TDL-B100' # 1 User 118 | channel_norm_eval = False 119 | n_size_bwp_eval = 132 120 | batch_size_eval = 30 121 | batch_size_eval_small = 3 # for k-best-based baselines 122 | -------------------------------------------------------------------------------- /config/nrx_large_var_mcs_64qam_masking.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_large_var_mcs_64qam_masking' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [9, 14, 19] # QPSK and 16-QAM 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | mcs_var_mcs_masking = True # True enables LLR masking, False (or not specified) implements MCS-specific IO layers 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | training_schedule = { 79 | "num_iter": [1e6, 9e6], 80 | "learning_rate": [0.001, 0.001], 81 | "batch_size": [128, 128], 82 | "train_tx": [False, False], 83 | "min_training_snr_db": [[-2., -2.], [-1., -1.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 84 | "max_training_snr_db": [[12., 15.], [12., 12.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 85 | "double_readout": [True, True], # use additional MSE loss on h_hat 86 | "weighting_double_readout": [0.02, 0.01], # weighting between MSE & BCE loss 87 | "apply_multiloss": [True, True]} 88 | 89 | mcs_training_snr_db_offset = [[0.0, 4.0, 7.0], [0.0, 2.0, 3.5]] # first list is for 1 UE, second for 2 UEs 90 | 91 | num_iter_train_save = 1000 92 | max_ut_velocity = 56. 93 | min_ut_velocity = 0. 94 | channel_norm = False 95 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 96 | # UMi 97 | channel_type = 'UMi' 98 | eval_ebno_db_arr = [1.0, 4.0, 4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 99 | # TDL (for two UEs) 100 | #channel_type = 'DoubleTDLlow' 101 | xla = True # Activate XLA for the training loop 102 | tfrecord_filename = "na" # only relevant if training is done with a dataset 103 | double_readout = True 104 | 105 | [evaluation] 106 | # the following parameters are used during evaluation 107 | snr_db_eval_min = -3 108 | snr_db_eval_max = 10 109 | snr_db_eval_stepsize = 1 110 | max_ut_velocity_eval = 56. 111 | min_ut_velocity_eval = 56. 112 | cfo_offset_ppm_eval = 0.0 113 | tfrecord_filename_eval = "na" 114 | channel_type_eval = "DoubleTDLlow" # 2 Users 115 | #channel_type_eval = 'TDL-B100' # 1 User 116 | channel_norm_eval = False 117 | n_size_bwp_eval = 132 118 | batch_size_eval = 30 119 | batch_size_eval_small = 3 # for k-best-based baselines 120 | -------------------------------------------------------------------------------- /config/nrx_rt.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_rt' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e6, 9e6], 83 | "learning_rate": [0.001, 0.001], 84 | "batch_size": [128, 128], 85 | "train_tx": [False, False], 86 | # we train for a wide SNR range 87 | "min_training_snr_db": [[0., 0.],[1., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "max_training_snr_db": [[10., 15.],[7., 12.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 89 | "double_readout": [True, True], # use additional MSE loss on h_hat 90 | "apply_multiloss": [False, False], 91 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 92 | 93 | num_iter_train_save = 1000 94 | max_ut_velocity = 56. 95 | min_ut_velocity = 0. 96 | channel_norm = False 97 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 98 | # UMi 99 | channel_type = 'UMi' 100 | eval_ebno_db_arr = [4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 101 | # TDL (for two UEs) 102 | #channel_type = 'DoubleTDLlow' 103 | xla = True # Activate XLA for the training loop 104 | tfrecord_filename = "na" # only relevant if training is done with a dataset 105 | 106 | [evaluation] 107 | # the following parameters are used during evaluation 108 | snr_db_eval_min = -2 109 | snr_db_eval_max = 8 110 | snr_db_eval_stepsize = 1 111 | max_ut_velocity_eval = 56 112 | min_ut_velocity_eval = 56 113 | cfo_offset_ppm_eval = 0.0 114 | tfrecord_filename_eval = "na" 115 | channel_type_eval = "DoubleTDLlow" # 2 Users 116 | #channel_type_eval = 'TDL-B100' # 1 User 117 | channel_norm_eval = False 118 | n_size_bwp_eval = 132 119 | batch_size_eval = 30 120 | batch_size_eval_small = 3 # for k-best-based baselines 121 | -------------------------------------------------------------------------------- /config/nrx_rt_64qam.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_rt_64qam' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [19] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e6, 9e6], 83 | "learning_rate": [0.001, 0.001], 84 | "batch_size": [128, 128], 85 | "train_tx": [False, False], 86 | # we train for a wide SNR range 87 | "min_training_snr_db": [[0., 0.],[2., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "max_training_snr_db": [[15., 20.],[15., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 89 | "double_readout": [True, True], # use additional MSE loss on h_hat 90 | "apply_multiloss": [False, False], 91 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 92 | 93 | num_iter_train_save = 1000 94 | max_ut_velocity = 56. 95 | min_ut_velocity = 0. 96 | channel_norm = False 97 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 98 | # UMi 99 | channel_type = 'UMi' 100 | eval_ebno_db_arr = [4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 101 | # TDL (for two UEs) 102 | #channel_type = 'DoubleTDLlow' 103 | xla = True # Activate XLA for the training loop 104 | tfrecord_filename = "na" # only relevant if training is done with a dataset 105 | 106 | [evaluation] 107 | # the following parameters are used during evaluation 108 | snr_db_eval_min = -2 109 | snr_db_eval_max = 12 110 | snr_db_eval_stepsize = 1 111 | max_ut_velocity_eval = 56 112 | min_ut_velocity_eval = 56 113 | cfo_offset_ppm_eval = 0.0 114 | tfrecord_filename_eval = "na" 115 | channel_type_eval = "DoubleTDLlow" # 2 Users 116 | #channel_type_eval = 'TDL-B100' # 1 User 117 | channel_norm_eval = False 118 | n_size_bwp_eval = 132 119 | batch_size_eval = 30 120 | batch_size_eval_small = 3 # for k-best-based baselines 121 | -------------------------------------------------------------------------------- /config/nrx_rt_qpsk.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_rt_qpsk' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [9] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e6, 7e6], 83 | "learning_rate": [0.001, 0.001], 84 | "batch_size": [128, 128], 85 | "train_tx": [False, False], 86 | "min_training_snr_db": [[-2., 0.],[-1., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[10., 10.],[3., 4.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [True, True], # use additional MSE loss on h_hat 89 | "apply_multiloss": [False, False], 90 | "weighting_double_readout": [0.02, 0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = False 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'UMi' 99 | eval_ebno_db_arr = [1.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | # TDL (for two UEs) 101 | #channel_type = 'DoubleTDLlow' 102 | xla = True # Activate XLA for the training loop 103 | tfrecord_filename = "na" # only relevant if training is done with a dataset 104 | 105 | [evaluation] 106 | # the following parameters are used during evaluation 107 | snr_db_eval_min = -3 108 | snr_db_eval_max = 8 109 | snr_db_eval_stepsize = 1 110 | max_ut_velocity_eval = 56 111 | min_ut_velocity_eval = 56 112 | cfo_offset_ppm_eval = 0.0 113 | tfrecord_filename_eval = "na" 114 | channel_type_eval = "DoubleTDLlow" # 2 Users 115 | #channel_type_eval = 'TDL-B100' # 1 User 116 | channel_norm_eval = False 117 | n_size_bwp_eval = 132 118 | batch_size_eval = 30 119 | batch_size_eval_small = 3 # for k-best-based baselines 120 | -------------------------------------------------------------------------------- /config/nrx_rt_var_mcs.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_rt_var_mcs' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [9, 14] # QPSK and 16-QAM 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | # quantization and other custom layer types 71 | layer_type_dense = "dense" 72 | layer_type_conv = "sepconv" # or "conv" 73 | layer_type_readout = "dense" 74 | nrx_dtype = tf.float32 75 | 76 | [training] 77 | # Each parameter in the training schedule is defined as list 78 | # The training loops over these parameters, i.e., performs num_iter[i] 79 | # SGD iterations for the ith set of parameters 80 | training_schedule = { 81 | "num_iter": [1e5, 2e6, 7e6], 82 | "learning_rate": [0.0005, 0.001, 0.0005], 83 | "batch_size": [128, 128, 128], 84 | "train_tx": [False, False, False], 85 | "min_training_snr_db": [[-2.,-2.], [-2., -2.], [-2., 0.0]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 86 | "max_training_snr_db": [[12.,14.], [8., 10.], [3., 5.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "double_readout": [True, True, True], # use additional MSE loss on h_hat 88 | "weighting_double_readout": [0.02, 0.02, 0.01], # weighting between MSE & BCE loss 89 | "apply_multiloss": [False, False, False]} 90 | 91 | mcs_training_snr_db_offset = [[0.0, 4.0], [0.0, 2.]] # first list is for 1 UE, second for 2 UEs 92 | 93 | num_iter_train_save = 1000 94 | max_ut_velocity = 56. 95 | min_ut_velocity = 0. 96 | channel_norm = False 97 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 98 | # UMi 99 | channel_type = 'UMi' 100 | eval_ebno_db_arr = [1.0, 4.0] # EbNo to evaluate model for each MCS during training every 1k iterations 101 | # TDL (for two UEs) 102 | #channel_type = 'DoubleTDLlow' 103 | xla = True # Activate XLA for the training loop 104 | tfrecord_filename = "na" # only relevant if training is done with a dataset 105 | double_readout = True 106 | 107 | [evaluation] 108 | # the following parameters are used during evaluation 109 | snr_db_eval_min = -3 110 | snr_db_eval_max = 8 111 | snr_db_eval_stepsize = 1 112 | max_ut_velocity_eval = 56 113 | min_ut_velocity_eval = 56 114 | cfo_offset_ppm_eval = 0.0 115 | tfrecord_filename_eval = "na" 116 | channel_type_eval = "DoubleTDLlow" # 2 Users 117 | #channel_type_eval = 'TDL-B100' # 1 User 118 | channel_norm_eval = False 119 | n_size_bwp_eval = 132 120 | batch_size_eval = 30 121 | batch_size_eval_small = 3 # for k-best-based baselines 122 | -------------------------------------------------------------------------------- /config/nrx_site_specific.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_site_specific' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e3], 83 | "learning_rate": [0.001], 84 | "batch_size": [128], 85 | "train_tx": [False], 86 | "min_training_snr_db": [[0., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[10., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [False], # use additional MSE loss on h_hat 89 | "apply_multiloss": [False], 90 | "weighting_double_readout": [0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = True 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'Dataset' # requires pre-generated dataset 99 | eval_ebno_db_arr = [10.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "nrx_site_specific_train.tfrecord" # only relevant if training is done with a dataset 102 | random_subsampling = False 103 | 104 | [evaluation] 105 | # the following parameters are used during evaluation 106 | snr_db_eval_min = -3 107 | snr_db_eval_max = 16 108 | snr_db_eval_stepsize = 1 109 | max_ut_velocity_eval = 56 110 | min_ut_velocity_eval = 56 111 | cfo_offset_ppm_eval = 0.0 112 | channel_type_eval = "Dataset" 113 | tfrecord_filename_eval = "nrx_site_specific_eval.tfrecord" # only relevant if dataset is used 114 | channel_norm_eval = True 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | random_subsampling_eval = True 119 | -------------------------------------------------------------------------------- /config/nrx_site_specific_100k.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_site_specific_100k' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e5], 83 | "learning_rate": [0.001], 84 | "batch_size": [128], 85 | "train_tx": [False], 86 | "min_training_snr_db": [[0., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[10., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [False], # use additional MSE loss on h_hat 89 | "apply_multiloss": [False], 90 | "weighting_double_readout": [0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = True 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'Dataset' # requires pre-generated dataset 99 | eval_ebno_db_arr = [10.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "nrx_site_specific_train.tfrecord" # only relevant if training is done with a dataset 102 | random_subsampling = False 103 | 104 | [evaluation] 105 | # the following parameters are used during evaluation 106 | snr_db_eval_min = -3 107 | snr_db_eval_max = 21 108 | snr_db_eval_stepsize = 1 109 | max_ut_velocity_eval = 56 110 | min_ut_velocity_eval = 56 111 | cfo_offset_ppm_eval = 0.0 112 | channel_type_eval = "Dataset" 113 | tfrecord_filename_eval = "nrx_site_specific_eval.tfrecord" # only relevant if dataset is used 114 | channel_norm_eval = True 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | random_subsampling_eval = True 119 | -------------------------------------------------------------------------------- /config/nrx_site_specific_baseline.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_site_specific_baseline' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 2 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 2 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64],[64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [0], 83 | "learning_rate": [0.001], 84 | "batch_size": [128], 85 | "train_tx": [False], 86 | "min_training_snr_db": [[0., 2.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[5., 7.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [False], # use additional MSE loss on h_hat 89 | "apply_multiloss": [False], 90 | "weighting_double_readout": [0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = True 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'UMi' # requires pre-generated dataset 99 | eval_ebno_db_arr = [3.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "" # only relevant if training is done with a dataset 102 | # random_subsampling = True 103 | 104 | [evaluation] 105 | # the following parameters are used during evaluation 106 | snr_db_eval_min = -3 107 | snr_db_eval_max = 16 108 | snr_db_eval_stepsize = 1 109 | max_ut_velocity_eval = 56 110 | min_ut_velocity_eval = 56 111 | cfo_offset_ppm_eval = 0.0 112 | channel_type_eval = "Dataset" 113 | tfrecord_filename_eval = "nrx_site_specific_eval.tfrecord" # only relevant if dataset is used 114 | channel_norm_eval = True 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | random_subsampling_eval = True 119 | -------------------------------------------------------------------------------- /config/nrx_site_specific_baseline_large.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_site_specific_baseline_large' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e5], 83 | "learning_rate": [0.001], 84 | "batch_size": [128], 85 | "train_tx": [False], 86 | "min_training_snr_db": [[0., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[10., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [False], # use additional MSE loss on h_hat 89 | "apply_multiloss": [False], 90 | "weighting_double_readout": [0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = True 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'Dataset' # requires pre-generated dataset 99 | eval_ebno_db_arr = [10.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "nrx_site_specific_train.tfrecord" # only relevant if training is done with a dataset 102 | random_subsampling = False 103 | 104 | [evaluation] 105 | # the following parameters are used during evaluation 106 | snr_db_eval_min = -3 107 | snr_db_eval_max = 16 108 | snr_db_eval_stepsize = 1 109 | max_ut_velocity_eval = 56 110 | min_ut_velocity_eval = 56 111 | cfo_offset_ppm_eval = 0.0 112 | channel_type_eval = "Dataset" 113 | tfrecord_filename_eval = "nrx_site_specific_eval.tfrecord" # only relevant if dataset is used 114 | channel_norm_eval = True 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | random_subsampling_eval = True 119 | -------------------------------------------------------------------------------- /config/nrx_site_specific_large.cfg: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | [global] 12 | label = 'nrx_site_specific_large' # all relevant files such as weights will use this label 13 | ebno = True # activate rate-adjusted SNR 14 | 15 | [system] 16 | n_size_bwp = 4 17 | num_rx_antennas = 4 18 | mcs_index = [14] 19 | mcs_table = 1 20 | carrier_frequency = 2140000000.0 21 | subcarrier_spacing = 30000.0 22 | n_start_grid = 0 23 | slot_number = 0 24 | frame_number = 0 25 | cyclic_prefix = 'normal' 26 | precoding = 'codebook' 27 | n_cell_id = 1 28 | tpmi = 2 29 | symbol_allocation = [0, 14] 30 | num_antenna_ports = 2 31 | dmrs_mapping_type = "A" 32 | dmrs_config_type = 1 33 | dmrs_type_a_position = 2 34 | dmrs_additional_position = 1 35 | dmrs_length = 1 36 | dmrs_nid = [[1, 1], [1, 1]] 37 | n_scid = 1 38 | num_cdm_groups_without_data = 2 39 | verbose = False 40 | dmrs_port_sets = [[0], [2]] 41 | n_rntis = [1, 1] 42 | n_ids = [1, 1] 43 | 44 | [baseline] 45 | demapping_type = 'maxlog' 46 | num_bp_iter = 20 47 | cn_type = 'boxplus' 48 | # For large num_prbs (>100), a low reduced complexity 49 | # LMMSE estimator is used where LMMSE is only performed over the 50 | # lmmse_num_prbs PRBS 51 | # if set to -1, the splitting parameters are calculated via an heursitic 52 | # the target of the heuristic is to find the best split such that 53 | # the resulting LMMSE is done over at least 20 PRBs 54 | lmmse_num_prbs = -1 # n_size_bwp must be multiple of this constant 55 | 56 | [neural_receiver] 57 | num_nrx_iter = 8 # defines number of cgnn_it stages 58 | num_nrx_iter_eval = 8 # iterations used for evaluation; must be <= num_nrx_iter 59 | d_s = 56 # feature space dimensions; effectively, defines num filter kernels (or neurons) for all components 60 | num_units_init = [128, 128] # num filter kernels for input CNN (each list entry defines one layer) 61 | num_units_agg = [[64], [64], [64], [64],[64], [64], [64], [64]] # number of neurons of state aggregation MLP (each list entry defines one layer) 62 | num_units_state = [[128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128], [128, 128]] # num filter kernels for stage update CNN (each list entry defines one layer) 63 | num_units_readout = [128] # number of neurons of state aggregation MLP (each list entry defines one layer) 64 | max_num_tx = 2 # max number of active DMRS ports 65 | min_num_tx = 1 # only relevant during training for random user sampling 66 | initial_chest = "ls" # "None" deactivates initial LS estimation 67 | custom_constellation = False # activates trainable transmitter 68 | mask_pilots = False # mask DMRS positions for e2e experiments 69 | 70 | 71 | # quantization and other custom layer types 72 | layer_type_dense = "dense" 73 | layer_type_conv = "sepconv" # or "conv" 74 | layer_type_readout = "dense" 75 | nrx_dtype = tf.float32 76 | 77 | [training] 78 | # Each parameter in the training schedule is defined as list 79 | # The training loops over these parameters, i.e., performs num_iter[i] 80 | # SGD iterations for the ith set of parameters 81 | training_schedule = { 82 | "num_iter": [1e3], 83 | "learning_rate": [0.001], 84 | "batch_size": [128], 85 | "train_tx": [False], 86 | "min_training_snr_db": [[0., 0.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 87 | "max_training_snr_db": [[10., 15.]], # 1 / 2 active UEs, is Eb/No [dB] if ebno==True 88 | "double_readout": [False], # use additional MSE loss on h_hat 89 | "apply_multiloss": [True], # keep multiloss also for site-specific 90 | "weighting_double_readout": [0.01]} # weighting between MSE & BCE loss 91 | 92 | num_iter_train_save = 1000 93 | max_ut_velocity = 56. 94 | min_ut_velocity = 0. 95 | channel_norm = True 96 | cfo_offset_ppm = 0.0 # randomly sampled in [-cfo_offset_ppm, cfo_offset_ppm] 97 | # UMi 98 | channel_type = 'Dataset' # requires pre-generated dataset 99 | eval_ebno_db_arr = [10.0] # EbNo to evaluate model for each MCS during training every 1k iterations 100 | xla = True # Activate XLA for the training loop 101 | tfrecord_filename = "nrx_site_specific_train.tfrecord" # only relevant if training is done with a dataset 102 | random_subsampling = False 103 | 104 | [evaluation] 105 | # the following parameters are used during evaluation 106 | snr_db_eval_min = -3 107 | snr_db_eval_max = 16 108 | snr_db_eval_stepsize = 1 109 | max_ut_velocity_eval = 56 110 | min_ut_velocity_eval = 56 111 | cfo_offset_ppm_eval = 0.0 112 | channel_type_eval = "Dataset" 113 | tfrecord_filename_eval = "nrx_site_specific_eval.tfrecord" # only relevant if dataset is used 114 | channel_norm_eval = True 115 | n_size_bwp_eval = 132 116 | batch_size_eval = 30 117 | batch_size_eval_small = 3 # for k-best-based baselines 118 | random_subsampling_eval = True 119 | -------------------------------------------------------------------------------- /misc/nrx_bler_results.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/misc/nrx_bler_results.png -------------------------------------------------------------------------------- /misc/nrx_latency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/misc/nrx_latency.png -------------------------------------------------------------------------------- /misc/nrx_overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/misc/nrx_overview.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | sionna==0.18.0 2 | matplotlib 3 | pandas 4 | onnx==1.16.2 5 | tf2onnx 6 | polygraphy 7 | mitsuba==3.5.2 # not specified sionna==0.18.0 8 | -------------------------------------------------------------------------------- /results/e2e_baseline_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/e2e_baseline_results -------------------------------------------------------------------------------- /results/e2e_large_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/e2e_large_results -------------------------------------------------------------------------------- /results/e2e_rt_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/e2e_rt_results -------------------------------------------------------------------------------- /results/mixed_mcs_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/mixed_mcs_results -------------------------------------------------------------------------------- /results/nrx_large_64qam_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_64qam_results -------------------------------------------------------------------------------- /results/nrx_large_qpsk_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_qpsk_results -------------------------------------------------------------------------------- /results/nrx_large_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_results -------------------------------------------------------------------------------- /results/nrx_large_sweep_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_sweep_results -------------------------------------------------------------------------------- /results/nrx_large_var_mcs_64qam_masking_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_var_mcs_64qam_masking_results -------------------------------------------------------------------------------- /results/nrx_large_var_mcs_masking_sweep_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_var_mcs_masking_sweep_results -------------------------------------------------------------------------------- /results/nrx_large_var_mcs_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_large_var_mcs_results -------------------------------------------------------------------------------- /results/nrx_rt_64qam_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_rt_64qam_results -------------------------------------------------------------------------------- /results/nrx_rt_qpsk_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_rt_qpsk_results -------------------------------------------------------------------------------- /results/nrx_rt_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_rt_results -------------------------------------------------------------------------------- /results/nrx_rt_var_mcs_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_rt_var_mcs_results -------------------------------------------------------------------------------- /results/nrx_site_specific_100k_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_100k_results -------------------------------------------------------------------------------- /results/nrx_site_specific_baseline_large_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_baseline_large_results -------------------------------------------------------------------------------- /results/nrx_site_specific_baseline_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_baseline_results -------------------------------------------------------------------------------- /results/nrx_site_specific_large_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_large_results -------------------------------------------------------------------------------- /results/nrx_site_specific_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_results -------------------------------------------------------------------------------- /results/nrx_site_specific_sweep_results: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/results/nrx_site_specific_sweep_results -------------------------------------------------------------------------------- /scripts/compute_cov_mat.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 4 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 5 | # 6 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 7 | # property and proprietary rights in and to this material, related 8 | # documentation and any modifications thereto. Any use, reproduction, 9 | # disclosure or distribution of this material and related documentation 10 | # without an express license agreement from NVIDIA CORPORATION or 11 | # its affiliates is strictly prohibited. 12 | 13 | # This script computes the covariance matrix for LMMSE channel estimation 14 | # the matrices are stored in the weights/ folder 15 | 16 | #################################################################### 17 | # Parse args 18 | #################################################################### 19 | 20 | import argparse 21 | 22 | parser = argparse.ArgumentParser() 23 | 24 | parser.add_argument("-config_name", help="config filename", type=str) 25 | parser.add_argument("-num_samples", help="Number of samples", 26 | type=int, default=1000000) 27 | parser.add_argument("-gpu", help="GPU to use", type=int, default=0) 28 | parser.add_argument("-num_tx_eval", help="Number of active users", 29 | type=int, default=1) 30 | 31 | # Parse all arguments 32 | args = parser.parse_args() 33 | config_name = args.config_name 34 | num_tx_eval = args.num_tx_eval 35 | 36 | #################################################################### 37 | # Imports and GPU configuration 38 | #################################################################### 39 | 40 | import os 41 | # Avoid warnings from TensorFlow 42 | os.environ["CUDA_VISIBLE_DEVICES"] = f"{args.gpu}" 43 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 44 | 45 | import tensorflow as tf 46 | tf.get_logger().setLevel('ERROR') 47 | 48 | gpus = tf.config.list_physical_devices('GPU') 49 | try: 50 | print('Only GPU number', args.gpu, 'used.') 51 | tf.config.experimental.set_memory_growth(gpus[0], True) 52 | except RuntimeError as e: 53 | print(e) 54 | 55 | import sys 56 | sys.path.append('../') 57 | 58 | import sionna as sn 59 | sn.Config.xla_compat = True 60 | from sionna.channel import GenerateOFDMChannel, gen_single_sector_topology 61 | 62 | from utils import Parameters 63 | import numpy as np 64 | 65 | ################################################################## 66 | # Setup link 67 | ################################################################## 68 | parameters = Parameters(config_name, 69 | training=False, 70 | num_tx_eval=num_tx_eval, 71 | system='nrx', 72 | compute_cov=True) #load UMi channel in any case 73 | 74 | batch_size = parameters.batch_size_eval 75 | NUM_SAMPLES = args.num_samples 76 | # run multiple iterations to limit the batchsize/memory requirements 77 | NUM_IT = int((NUM_SAMPLES//batch_size)+1) 78 | 79 | channel_model = parameters.channel_model 80 | 81 | # OFDM channel in frequency domain 82 | gen_ofdm_channel = GenerateOFDMChannel( 83 | channel_model, 84 | parameters.transmitters[0]._resource_grid, 85 | normalize_channel=True) 86 | 87 | ################################################################# 88 | # Evaluate covariance matrices 89 | ################################################################# 90 | 91 | # Function that generates a batch of channel samples. 92 | # A new topology is sampled for every batch and for every batch example. 93 | def sample_channel(batch_size): 94 | # Sample a random network topology for each 95 | # batch example 96 | topology = gen_single_sector_topology(batch_size, 1, 'umi', 97 | min_ut_velocity=parameters.min_ut_velocity, 98 | max_ut_velocity=parameters.max_ut_velocity) 99 | channel_model.set_topology(*topology) 100 | 101 | # Sample channel frequency response 102 | # [batch size, 1, num_rx_ant, 1, 1, num_ofdm_symbols, fft_size] 103 | h_freq = gen_ofdm_channel(batch_size) 104 | # [batch size, num_rx_ant, num_ofdm_symbols, fft_size] 105 | h_freq = h_freq[:,0,:,0,0] 106 | 107 | return h_freq 108 | 109 | @tf.function(jit_compile=True) # No XLA for better precision 110 | def estimate_cov_mats(batch_size, num_it): 111 | rg = parameters.transmitters[0]._resource_grid 112 | freq_cov_mat = tf.zeros([rg.fft_size, rg.fft_size], tf.complex64) 113 | time_cov_mat = tf.zeros([rg.num_ofdm_symbols, rg.num_ofdm_symbols], 114 | tf.complex64) 115 | space_cov_mat = tf.zeros([parameters.num_rx_antennas, 116 | parameters.num_rx_antennas], tf.complex64) 117 | 118 | for _ in tf.range(num_it): 119 | # [batch size, num_rx_ant, num_ofdm_symbols, fft_size] 120 | h_samples = sample_channel(batch_size) 121 | # 122 | # Frequency covariance matrix estimation 123 | # 124 | # [batch size, num_rx_ant, fft_size, num_ofdm_symbols] 125 | h_samples_ = tf.transpose(h_samples, [0,1,3,2]) 126 | # [batch size, num_rx_ant, fft_size, fft_size] 127 | freq_cov_mat_ = tf.matmul(h_samples_, h_samples_, adjoint_b=True) 128 | # [fft_size, fft_size] 129 | freq_cov_mat_ = tf.reduce_mean(freq_cov_mat_, axis=(0,1)) 130 | # [fft_size, fft_size] 131 | freq_cov_mat += freq_cov_mat_ 132 | 133 | # 134 | # Time covariance matrix estimation 135 | # 136 | # [batch size, num_rx_ant, num_ofdm_symbols, num_ofdm_symbols] 137 | time_cov_mat_ = tf.matmul(h_samples, h_samples, adjoint_b=True) 138 | # [num_ofdm_symbols, num_ofdm_symbols] 139 | time_cov_mat_ = tf.reduce_mean(time_cov_mat_, axis=(0,1)) 140 | # [num_ofdm_symbols, num_ofdm_symbols] 141 | time_cov_mat += time_cov_mat_ 142 | 143 | # 144 | # Spatial covariance matrix estimation 145 | # 146 | # [batch size, num_ofdm_symbols, num_rx_ant, fft_size] 147 | h_samples_ = tf.transpose(h_samples, [0,2,1,3]) 148 | # [batch size, num_ofdm_symbols, num_rx_ant, num_rx_ant] 149 | space_cov_mat_ = tf.matmul(h_samples_, h_samples_, adjoint_b=True) 150 | # [num_rx_ant, num_rx_ant] 151 | space_cov_mat_ = tf.reduce_mean(space_cov_mat_, axis=(0,1)) 152 | # [num_rx_ant, num_rx_ant] 153 | space_cov_mat += space_cov_mat_ 154 | 155 | freq_cov_mat /= tf.complex(tf.cast(rg.num_ofdm_symbols*num_it, tf.float32), 156 | 0.0) 157 | time_cov_mat /= tf.complex(tf.cast(rg.fft_size*num_it, tf.float32), 0.0) 158 | space_cov_mat /= tf.complex(tf.cast(rg.fft_size*num_it, tf.float32), 0.0) 159 | return freq_cov_mat, time_cov_mat, space_cov_mat 160 | 161 | # 162 | # Run estimation 163 | # 164 | freq_cov_mat, time_cov_mat, space_cov_mat = estimate_cov_mats(batch_size, 165 | NUM_IT) 166 | freq_cov_mat = freq_cov_mat.numpy() 167 | time_cov_mat = time_cov_mat.numpy() 168 | space_cov_mat = space_cov_mat.numpy() 169 | 170 | # Saving covariance matrices 171 | # Save the time and frequency covariance matrices. 172 | np.save(f'../weights/{parameters.label}_freq_cov_mat', freq_cov_mat) 173 | np.save(f'../weights/{parameters.label}_time_cov_mat', time_cov_mat) 174 | np.save(f'../weights/{parameters.label}_space_cov_mat', space_cov_mat) 175 | -------------------------------------------------------------------------------- /scripts/evaluate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 4 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 5 | # 6 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 7 | # property and proprietary rights in and to this material, related 8 | # documentation and any modifications thereto. Any use, reproduction, 9 | # disclosure or distribution of this material and related documentation 10 | # without an express license agreement from NVIDIA CORPORATION or 11 | # its affiliates is strictly prohibited. 12 | 13 | # evaluate BLER of NRX and baseline systems 14 | # results are saved in files and can be visualized with the corresponding 15 | # jupyter notebooks 16 | 17 | #################################################################### 18 | # Parse args 19 | #################################################################### 20 | 21 | import argparse 22 | 23 | parser = argparse.ArgumentParser() 24 | 25 | # the config defines the sys parameters 26 | parser.add_argument("-config_name", help="config filename", type=str) 27 | # limits the number of target of block errors during the simulation 28 | parser.add_argument("-num_target_block_errors", 29 | help="Number of target block errors", type=int, default=500) 30 | parser.add_argument("-max_mc_iter", 31 | help="Maximum Monte Carlo iterations", 32 | type=int, default=500) 33 | parser.add_argument("-target_bler", 34 | help="Early stop BLER simulations at a specific target BLER", 35 | type=float, default=0.001) 36 | parser.add_argument("-num_cov_samples", 37 | help="Number of samples for covariance generation", type=int, default=100000) 38 | parser.add_argument("-gpu", help="GPU to use", type=int, default=0) 39 | parser.add_argument("-num_tx_eval", 40 | help="Number of active users", 41 | type=int, nargs='+', default=-1) 42 | parser.add_argument("-mcs_arr_eval_idx", 43 | help="Select the MCS array index for evaluation. Use -1 to evaluate all MCSs.", type=int, default=-1) 44 | parser.add_argument("-eval_nrx_only", help="Only evaluate the NN", 45 | action="store_true", default=False) 46 | parser.add_argument("-debug", help="Set debugging configuration", action="store_true", default=False) 47 | 48 | # Parse all arguments 49 | args = parser.parse_args() 50 | 51 | config_name = args.config_name 52 | max_mc_iter = args.max_mc_iter 53 | num_target_block_errors = args.num_target_block_errors 54 | eval_nrx_only = args.eval_nrx_only 55 | num_cov_samples = args.num_cov_samples 56 | gpu = args.gpu 57 | target_bler = args.target_bler 58 | num_tx_eval = args.num_tx_eval 59 | mcs_arr_eval_idx = args.mcs_arr_eval_idx 60 | 61 | distribute = None # use "all" to distribute over multiple GPUs 62 | 63 | #################################################################### 64 | # Imports and GPU configuration 65 | #################################################################### 66 | 67 | import os 68 | # Avoid warnings from TensorFlow 69 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 70 | 71 | import tensorflow as tf 72 | tf.get_logger().setLevel('ERROR') 73 | 74 | gpus = tf.config.list_physical_devices('GPU') 75 | 76 | if distribute != "all": 77 | try: 78 | tf.config.set_visible_devices(gpus[args.gpu], 'GPU') 79 | print('Only GPU number', args.gpu, 'used.') 80 | tf.config.experimental.set_memory_growth(gpus[args.gpu], True) 81 | except RuntimeError as e: 82 | print(e) 83 | 84 | import sys 85 | sys.path.append('../') 86 | 87 | import sionna as sn 88 | from sionna.utils import sim_ber 89 | from utils import E2E_Model, Parameters, load_weights 90 | import numpy as np 91 | import pickle 92 | from os.path import exists 93 | 94 | if args.debug: 95 | tf.config.run_functions_eagerly(True) 96 | 97 | ################################################################## 98 | # Run evaluations 99 | ################################################################## 100 | 101 | # dummy parameters to access filename and to load results 102 | sys_parameters = Parameters(config_name, 103 | training=True, 104 | system='dummy') # dummy system only to load config 105 | 106 | # two different batch sizes can be configured 107 | # the small one is used for the highly complex K-best-based receivers 108 | # otherwise OOM errors occur 109 | batch_size = sys_parameters.batch_size_eval 110 | batch_size_small = sys_parameters.batch_size_eval_small 111 | 112 | # results are directly saved in files 113 | results_filename = f"{sys_parameters.label}_results" 114 | results_filename = "../results/" + results_filename 115 | 116 | if exists(results_filename): 117 | print(f"### File '{results_filename}' found. " \ 118 | "It will be updated with the new results.") 119 | with open(results_filename, 'rb') as f: 120 | ebno_db, BERs, BLERs = pickle.load(f) 121 | else: 122 | print(f"### No file '{results_filename}' found. One will be created.") 123 | ebno_db = np.arange(sys_parameters.snr_db_eval_min, 124 | sys_parameters.snr_db_eval_max, 125 | sys_parameters.snr_db_eval_stepsize) 126 | BERs = {} 127 | BLERs = {} 128 | 129 | # evaluate for different number of active transmitters 130 | if num_tx_eval == -1: 131 | num_tx_evals = np.arange(sys_parameters.min_num_tx, 132 | sys_parameters.max_num_tx+1, 1) 133 | else: 134 | if isinstance(num_tx_eval, int): 135 | num_tx_evals = [num_tx_eval] 136 | elif isinstance(num_tx_eval, (list, tuple)): 137 | num_tx_evals = num_tx_eval 138 | else: 139 | raise ValueError("num_tx_eval must be int or list of ints.") 140 | 141 | if mcs_arr_eval_idx == -1: 142 | mcs_arr_eval_idxs = list(range(len(sys_parameters.mcs_index))) 143 | else: 144 | if isinstance(mcs_arr_eval_idx, int): 145 | mcs_arr_eval_idxs = [mcs_arr_eval_idx] 146 | elif isinstance(mcs_arr_eval_idx, (list, tuple)): 147 | mcs_arr_eval_idxs = mcs_arr_eval_idx 148 | else: 149 | raise ValueError("mcs_arr_eval_idx must be int or list of ints.") 150 | 151 | print(f"Evaluating for {num_tx_evals} active users and mcs_index elements {mcs_arr_eval_idxs}.") 152 | 153 | # the evaluation can loop over multiple number of active DMRS ports / users 154 | for num_tx_eval in num_tx_evals: 155 | 156 | # Generate covariance matrices for LMMSE-based baselines 157 | if not eval_nrx_only: 158 | print("Generating cov matrix.") 159 | os.system(f"python compute_cov_mat.py -config_name {config_name} -gpu {gpu} -num_samples {num_cov_samples} -num_tx_eval {num_tx_eval}") 160 | 161 | # Loop over all evaluation MCS indices 162 | for mcs_arr_eval_idx in mcs_arr_eval_idxs: 163 | 164 | # 165 | # Neural receiver 166 | # 167 | sn.Config.xla_compat = True 168 | sys_parameters = Parameters(config_name, 169 | training=False, 170 | num_tx_eval=num_tx_eval, 171 | system='nrx') 172 | 173 | # check channel types for consistency 174 | if sys_parameters.channel_type == 'TDL-B100': 175 | assert num_tx_eval == 1,\ 176 | "Channel model 'TDL-B100' only works with one transmitter" 177 | elif sys_parameters.channel_type in ("DoubleTDLlow", "DoubleTDLmedium", 178 | "DoubleTDLhigh"): 179 | assert num_tx_eval == 2,\ 180 | "Channel model 'DoubleTDL' only works with two transmitters exactly" 181 | e2e_nn = E2E_Model(sys_parameters, training=False, mcs_arr_eval_idx=mcs_arr_eval_idx) 182 | 183 | print("\nRunning: " + sys_parameters.system) 184 | # Run once and load the weights 185 | e2e_nn(1, 1.) 186 | filename = f'../weights/{sys_parameters.label}_weights' 187 | load_weights(e2e_nn, filename) 188 | 189 | # and set number iterations for evaluation 190 | e2e_nn._receiver._neural_rx.num_it = sys_parameters.num_nrx_iter_eval 191 | 192 | # Start sim 193 | ber, bler = sim_ber(e2e_nn, 194 | graph_mode="xla", 195 | ebno_dbs=ebno_db, 196 | max_mc_iter=max_mc_iter, 197 | num_target_block_errors=num_target_block_errors, 198 | batch_size=batch_size, 199 | distribute=distribute, 200 | target_bler=target_bler, 201 | early_stop=True, 202 | forward_keyboard_interrupt=True) 203 | BERs[e2e_nn._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 204 | BLERs[e2e_nn._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 205 | with open(results_filename, "wb") as f: 206 | pickle.dump([ebno_db, BERs, BLERs], f) 207 | sn.Config.xla_compat = False 208 | 209 | # 210 | # Baseline: LS estimation/lin interpolation + LMMSE detection 211 | # 212 | if not eval_nrx_only: 213 | sn.Config.xla_compat = True 214 | sys_parameters = Parameters(config_name, 215 | training=False, 216 | num_tx_eval=num_tx_eval, 217 | system='baseline_lslin_lmmse') 218 | e2e_baseline = E2E_Model(sys_parameters, training=False, 219 | mcs_arr_eval_idx=mcs_arr_eval_idx) 220 | 221 | print("\nRunning: " + sys_parameters.system) 222 | ber, bler = sim_ber(e2e_baseline, 223 | graph_mode="xla", 224 | ebno_dbs=ebno_db, 225 | max_mc_iter=max_mc_iter, 226 | num_target_block_errors=num_target_block_errors, 227 | target_bler=target_bler, 228 | batch_size=batch_size, 229 | distribute=distribute, 230 | early_stop=True, 231 | forward_keyboard_interrupt=True) 232 | BERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 233 | BLERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 234 | with open(results_filename, "wb") as f: 235 | pickle.dump([ebno_db, BERs, BLERs], f) 236 | sn.Config.xla_compat = False 237 | else: 238 | print("skipping LSlin & LMMSE") 239 | # 240 | # Baseline: LMMSE estimation/interpolation + K-Best detection 241 | # 242 | if not eval_nrx_only: 243 | sn.Config.xla_compat = False 244 | sys_parameters = Parameters(config_name, 245 | training=False, 246 | num_tx_eval=num_tx_eval, 247 | system = 'baseline_lmmse_kbest') 248 | e2e_baseline = E2E_Model(sys_parameters, training=False, 249 | mcs_arr_eval_idx=mcs_arr_eval_idx) 250 | 251 | print("\nRunning: " + sys_parameters.system) 252 | ber, bler = sim_ber(e2e_baseline, 253 | graph_mode="graph", 254 | ebno_dbs=ebno_db, 255 | max_mc_iter=max_mc_iter, 256 | num_target_block_errors=num_target_block_errors, 257 | target_bler=target_bler, 258 | batch_size=batch_size_small, # must be small for large PRBs 259 | #distribute=distribute, # somehow does not compile 260 | early_stop=True, 261 | forward_keyboard_interrupt=True) 262 | BERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 263 | BLERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 264 | with open(results_filename, "wb") as f: 265 | pickle.dump([ebno_db, BERs, BLERs], f) 266 | sn.Config.xla_compat = False 267 | else: 268 | print("skipping LMMSE & KBest") 269 | 270 | # Uncomment to simulate other baselines 271 | # 272 | # Baseline: Perfect CSI + LMMSE 273 | # 274 | # currently not evaluated 275 | # if not eval_nrx_only: 276 | # sys_parameters = Parameters(config_name, 277 | # training=False, 278 | # num_tx_eval=num_tx_eval, 279 | # system='baseline_perf_csi_lmmse') 280 | # e2e_baseline = E2E_Model(sys_parameters, training=False, mcs_arr_eval_idx=mcs_arr_eval_idx) 281 | 282 | # print("\nRunning: " + sys_parameters.system) 283 | # ber, bler = sim_ber(e2e_baseline, 284 | # graph_mode="graph", 285 | # ebno_dbs=ebno_db, 286 | # max_mc_iter=max_mc_iter, # account for reduced bs 287 | # num_target_block_errors=num_target_block_errors, 288 | # batch_size=batch_size, # must be small due to TF bug in K-best 289 | # early_stop=True) 290 | # BERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 291 | # BLERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 292 | # with open(results_filename, "wb") as f: 293 | # pickle.dump([ebno_db, BERs, BLERs], f) 294 | # else: 295 | # print("skipping Perfect CSI & LMMSE") 296 | 297 | # 298 | # Baseline: LMMSE estimation/interpolation + LMMSE detection 299 | # 300 | # if not eval_nrx_only: 301 | # sn.Config.xla_compat = False 302 | # sys_parameters = Parameters(config_name, 303 | # training=False, 304 | # num_tx_eval=num_tx_eval, 305 | # system='baseline_lmmse_lmmse') 306 | # e2e_baseline = E2E_Model(sys_parameters, training=False, mcs_arr_eval_idx=mcs_arr_eval_idx) 307 | 308 | # print("Running: " + sys_parameters.system) 309 | # ber, bler = sim_ber(e2e_baseline, 310 | # graph_mode="graph", 311 | # ebno_dbs=ebno_db, 312 | # max_mc_iter=max_mc_iter, # account for reduced bs 313 | # num_target_block_errors=num_target_block_errors, 314 | # #target_bler=target_bler, 315 | # batch_size=batch_size_small, # must be small due to TF bug in K-best 316 | # #distribute=distribute, 317 | # early_stop=True, 318 | # forward_keyboard_interrupt=True) 319 | # BERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 320 | # BLERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 321 | # with open(results_filename, "wb") as f: 322 | # pickle.dump([ebno_db, BERs, BLERs], f) 323 | # sn.Config.xla_compat = False 324 | # else: 325 | # print("skipping LMMSE") 326 | # sys_name = f"Baseline - LMMSE+LMMSE" 327 | 328 | # 329 | # Baseline: Perfect CSI + K-Best detection 330 | # 331 | if not eval_nrx_only: 332 | sn.Config.xla_compat = False 333 | sys_parameters = Parameters(config_name, 334 | training=False, 335 | num_tx_eval=num_tx_eval, 336 | system='baseline_perf_csi_kbest') 337 | e2e_baseline = E2E_Model(sys_parameters, training=False, 338 | mcs_arr_eval_idx=mcs_arr_eval_idx) 339 | 340 | print("\nRunning: " + sys_parameters.system) 341 | ber, bler = sim_ber(e2e_baseline, 342 | graph_mode="graph", 343 | ebno_dbs=ebno_db, 344 | max_mc_iter=max_mc_iter, # account for reduced bs 345 | num_target_block_errors=num_target_block_errors, 346 | target_bler=target_bler, 347 | batch_size=batch_size_small, # must be small due to TF bug in K-best 348 | distribute=distribute, 349 | early_stop=True, 350 | forward_keyboard_interrupt=True) 351 | BERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = ber 352 | BLERs[e2e_baseline._sys_name, num_tx_eval, mcs_arr_eval_idx] = bler 353 | with open(results_filename, "wb") as f: 354 | pickle.dump([ebno_db, BERs, BLERs], f) 355 | sn.Config.xla_compat = False 356 | else: 357 | print("skipping Perfect CSI & K-Best") 358 | 359 | 360 | -------------------------------------------------------------------------------- /scripts/export_onnx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 4 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 5 | # 6 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 7 | # property and proprietary rights in and to this material, related 8 | # documentation and any modifications thereto. Any use, reproduction, 9 | # disclosure or distribution of this material and related documentation 10 | # without an express license agreement from NVIDIA CORPORATION or 11 | # its affiliates is strictly prohibited. 12 | 13 | 14 | # Exports TensorFlow/Keras model to ONNX 15 | # The scripts also runs a TRT latency evaluation. 16 | # Different quantization levels can be selected towards the end of this script. 17 | 18 | #################################################################### 19 | # Parse args 20 | #################################################################### 21 | 22 | import argparse 23 | 24 | parser = argparse.ArgumentParser() 25 | 26 | parser.add_argument("-config_name", help="config filename", type=str) 27 | parser.add_argument("-gpu", help="GPU to use", type=int, default=0) 28 | parser.add_argument("-num_tx", help="Max number of active users", type=int, default=1) 29 | 30 | # Parse all arguments 31 | args = parser.parse_args() 32 | config_name = args.config_name 33 | gpu_num = args.gpu 34 | num_tx = args.num_tx 35 | 36 | #################################################################### 37 | # Imports and GPU configuration 38 | #################################################################### 39 | 40 | # Avoid warnings from TensorFlow 41 | import os 42 | os.environ["CUDA_VISIBLE_DEVICES"] = f"{args.gpu}" 43 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 44 | 45 | import tensorflow as tf 46 | tf.get_logger().setLevel('ERROR') 47 | 48 | gpus = tf.config.list_physical_devices('GPU') 49 | try: 50 | print('Only GPU number', args.gpu, 'used.') 51 | tf.config.experimental.set_memory_growth(gpus[0], True) 52 | except RuntimeError as e: 53 | print(e) 54 | 55 | import numpy as np 56 | 57 | import sys 58 | sys.path.append('../') 59 | 60 | from utils import Parameters, NeuralReceiverONNX, load_weights 61 | 62 | import tf2onnx 63 | import onnx 64 | from utils import * 65 | 66 | ################################################################## 67 | # Load Config 68 | ################################################################## 69 | print(f"Loading: {config_name}.") 70 | 71 | sys_parameters = Parameters(config_name, 72 | training=False, 73 | system='nrx', 74 | num_tx_eval=num_tx) 75 | 76 | if len(sys_parameters.transmitters)>1: 77 | print("Warning: mixedMCS currently not supported." \ 78 | "Using only first MCS scheme in list.") 79 | 80 | # to generate test data 81 | generator = DataGeneratorAerial(sys_parameters) 82 | evaluator = DataEvaluator(sys_parameters) 83 | 84 | # init neural receiver model 85 | neural_rx = NeuralReceiverONNX( 86 | num_it=sys_parameters.num_nrx_iter, 87 | d_s=sys_parameters.d_s, 88 | num_units_init=sys_parameters.num_units_init, 89 | num_units_agg=sys_parameters.num_units_agg, 90 | num_units_state=sys_parameters.num_units_state, 91 | num_units_readout=sys_parameters.num_units_readout, 92 | num_bits_per_symbol=sys_parameters.transmitters[0]._num_bits_per_symbol, 93 | layer_type_dense = sys_parameters.layer_type_dense, 94 | layer_type_conv = sys_parameters.layer_type_conv, 95 | layer_type_readout = sys_parameters.layer_type_readout, 96 | nrx_dtype = sys_parameters.nrx_dtype, 97 | num_tx=sys_parameters.max_num_tx, 98 | num_rx_ant=sys_parameters.num_rx_antennas) 99 | 100 | # define system parameters 101 | batch_size = 1 # for real-time typically bs=1 is required 102 | num_rx_ant = sys_parameters.num_rx_antennas 103 | num_prbs = sys_parameters.n_size_bwp_eval 104 | num_tx = sys_parameters.max_num_tx 105 | num_ofdm_symbol = sys_parameters.symbol_allocation[1] 106 | 107 | # get active DMRS positions of first PRB for each user 108 | # ignore empty pilots if multiple CDM groups are used 109 | rg = sys_parameters.transmitters[0].resource_grid.build_type_grid().numpy() 110 | # value of pilots used for transmission (filter empty pilots) 111 | pilots = sys_parameters.transmitters[0].resource_grid.pilot_pattern.pilots 112 | pilots = pilots.numpy() 113 | 114 | # generate NRX inputs data for dummy inference of the receiver 115 | nrx_inputs, bits, u, h = generator(batch_size, 0.) 116 | 117 | neural_rx(nrx_inputs); 118 | 119 | # load weights 120 | print("Loading pre-trained weights.") 121 | load_weights(neural_rx, f"../weights/{sys_parameters.label}_weights") 122 | 123 | # and set number iterations for evaluation 124 | neural_rx._cgnn.num_it = sys_parameters.num_nrx_iter_eval 125 | 126 | ################### 127 | # Save model via TF 128 | ################### 129 | # Remark: this is not strictly required for the ONNX export 130 | # Can be used for other inference pipelines 131 | 132 | neural_rx.save(f"../onnx_models/{sys_parameters.label}_tf") 133 | 134 | ################ 135 | # Export to ONNX 136 | ################ 137 | 138 | # we re-use the previously generated dummy data to infer all input shapes 139 | rx_slot,_,h_hat,_,dmrs_port_mask,dmrs_ofdm_pos,dmrs_subcarrier_pos = nrx_inputs 140 | 141 | s_rx = rx_slot.shape 142 | s_h = h_hat.shape 143 | s_dmrs_mask = dmrs_port_mask.shape 144 | s_dmrs_ofdm_pos = dmrs_ofdm_pos.shape 145 | s_dmrs_subc_pos = dmrs_subcarrier_pos.shape 146 | 147 | # activate dynamic shapes by setting shapes to None 148 | # the dynamic ranges of these dimensions must be specified in 'trtexec' later 149 | # in our case we target a dynamic number of subcarriers 150 | s_rx = [1, None, num_ofdm_symbol, num_rx_ant] 151 | s_h = [1, None, num_tx, num_rx_ant] 152 | 153 | input_signature =[ 154 | tf.TensorSpec(s_rx, tf.float32, name="rx_slot_real"), 155 | tf.TensorSpec(s_rx, tf.float32, name="rx_slot_imag"), 156 | tf.TensorSpec(s_h, tf.float32, name="h_hat_real"), 157 | tf.TensorSpec(s_h, tf.float32, name="h_hat_imag"), 158 | tf.TensorSpec(s_dmrs_mask, tf.float32, name="active_dmrs_ports"), 159 | tf.TensorSpec(s_dmrs_ofdm_pos, tf.int32, name="dmrs_ofdm_pos"), 160 | tf.TensorSpec(s_dmrs_subc_pos, tf.int32, name="dmrs_subcarrier_pos"),] 161 | 162 | # convert model 163 | print("---Converting ONNX model---") 164 | onnx_model, _ = tf2onnx.convert.from_keras(neural_rx, input_signature) 165 | 166 | # and save the ONNX model 167 | print("---Saving ONNX model---") 168 | onnx.save(onnx_model,f"../onnx_models/{sys_parameters.label}.onnx") 169 | 170 | ################################# 171 | # compile ONNX model with TRTExec 172 | ################################# 173 | batch_size = 1 174 | 175 | # ONNX can support dynamic shapes. 176 | # For this, [min, best, max] dimensions must be provided 177 | num_prbs = [num_prbs, 178 | num_prbs, 179 | num_prbs] # number of active PRBs 180 | 181 | num_dmrs_time = [len(dmrs_ofdm_pos[-1]), 182 | len(dmrs_ofdm_pos[-1]), 183 | len(dmrs_ofdm_pos[-1]), 184 | ] # number of DMRS symbols in time per UE 185 | 186 | num_dmrs_subcarrier = len(dmrs_subcarrier_pos[-1]) # number of pilots per PRB (and per ofdm symbol) per UE 187 | num_ofdm_symbol = sys_parameters.symbol_allocation[1] 188 | 189 | # Seems like best latency results are achieved if "opt" case equals "max" 190 | # scenario as we mainly care about worst case latency anyhow. 191 | 192 | num_pilots = [] 193 | for a,b in zip(num_prbs, num_dmrs_time): 194 | num_pilots.append(a * b * num_dmrs_subcarrier) 195 | 196 | trt_command = f'trtexec --fp16 '\ 197 | f'--onnx=../onnx_models/{sys_parameters.label}.onnx '\ 198 | f'--saveEngine=../onnx_models/{sys_parameters.label}.plan '#\ 199 | #f'--dumpProfile --separateProfileRun ' 200 | # for latest TensorRT versions, the flag "--preview=-fasterDynamicShapes0805" 201 | # might give a few additional us latency. 202 | # --int8 or --best is also an option for the dtype 203 | 204 | # add shapes 205 | for idx,s in enumerate((" --minShapes="," --optShapes="," --maxShapes=")): 206 | trt_command += s + \ 207 | f'rx_slot_real:{batch_size}x{num_prbs[idx]*12}x{num_ofdm_symbol}x{num_rx_ant},'\ 208 | f'rx_slot_imag:{batch_size}x{num_prbs[idx]*12}x{num_ofdm_symbol}x{num_rx_ant},'\ 209 | f'h_hat_real:{batch_size}x{num_pilots[idx]}x{num_tx}x{num_rx_ant},'\ 210 | f'h_hat_imag:{batch_size}x{num_pilots[idx]}x{num_tx}x{num_rx_ant}' 211 | print(trt_command) 212 | os.system(trt_command) 213 | 214 | print("Done!") 215 | -------------------------------------------------------------------------------- /scripts/train_neural_rx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 4 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 5 | # 6 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 7 | # property and proprietary rights in and to this material, related 8 | # documentation and any modifications thereto. Any use, reproduction, 9 | # disclosure or distribution of this material and related documentation 10 | # without an express license agreement from NVIDIA CORPORATION or 11 | # its affiliates is strictly prohibited. 12 | 13 | # training of the neural receiver for a given configuration file 14 | # the training loop can be found in utils.training_loop 15 | 16 | #################################################################### 17 | # Parse args 18 | #################################################################### 19 | 20 | import argparse 21 | from os.path import exists 22 | 23 | parser = argparse.ArgumentParser() 24 | # the config defines the sys parameters 25 | parser.add_argument("-config_name", help="config filename", type=str) 26 | # GPU to use 27 | parser.add_argument("-gpu", help="GPU to use", type=int, default=0) 28 | # Easier debugging with breakpoints when running the code eagerly 29 | parser.add_argument("-debug", help="Set debugging configuration", action="store_true", default=False) 30 | 31 | # Parse all arguments 32 | args = parser.parse_args() 33 | 34 | #################################################################### 35 | # Imports and GPU configuration 36 | #################################################################### 37 | 38 | # Avoid warnings from TensorFlow 39 | import os 40 | os.environ["CUDA_VISIBLE_DEVICES"] = f"{args.gpu}" 41 | os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' 42 | 43 | import tensorflow as tf 44 | tf.get_logger().setLevel('ERROR') 45 | 46 | gpus = tf.config.list_physical_devices('GPU') 47 | try: 48 | print('Only GPU number', args.gpu, 'used.') 49 | tf.config.experimental.set_memory_growth(gpus[0], True) 50 | except RuntimeError as e: 51 | print(e) 52 | 53 | import sys 54 | sys.path.append('../') 55 | 56 | from utils import E2E_Model, training_loop, Parameters, load_weights 57 | 58 | ################################################################## 59 | # Training parameters 60 | ################################################################## 61 | 62 | # all relevant parameters are defined in the config_file 63 | config_name = args.config_name 64 | 65 | # initialize system parameters 66 | sys_parameters = Parameters(config_name, 67 | system='nrx', 68 | training=True) 69 | label = f'{sys_parameters.label}' 70 | filename = '../weights/'+ label + '_weights' 71 | training_logdir = '../logs' # use TensorBoard to visualize 72 | training_seed = 42 73 | 74 | if args.debug: 75 | tf.config.run_functions_eagerly(True) 76 | training_logdir = training_logdir + "/debug" 77 | 78 | ################################################################# 79 | # Start training 80 | ################################################################# 81 | 82 | sys_training = E2E_Model(sys_parameters, training=True) 83 | sys_training(1, 1.) # run once to init weights in TensorFlow 84 | sys_training.summary() 85 | 86 | # load weights if the exists already 87 | if exists(filename): 88 | print("\nWeights exist already - loading stored weights.") 89 | load_weights(sys_training, filename) 90 | 91 | if hasattr(sys_parameters, 'mcs_training_snr_db_offset'): 92 | mcs_training_snr_db_offset = sys_parameters.mcs_training_snr_db_offset 93 | else: 94 | mcs_training_snr_db_offset = None 95 | 96 | if hasattr(sys_parameters, 'mcs_training_probs'): 97 | mcs_training_probs = sys_parameters.mcs_training_probs 98 | else: 99 | mcs_training_probs = None 100 | 101 | # run the training / weights are automatically saved 102 | # UEs' MCSs will be drawn randomly 103 | training_loop(sys_training, 104 | label=label, 105 | filename=filename, 106 | training_logdir=training_logdir, 107 | training_seed=training_seed, 108 | training_schedule=sys_parameters.training_schedule, 109 | eval_ebno_db_arr=sys_parameters.eval_ebno_db_arr, 110 | min_num_tx=sys_parameters.min_num_tx, 111 | max_num_tx=sys_parameters.max_num_tx, 112 | sys_parameters=sys_parameters, 113 | mcs_arr_training_idx=list(range(len(sys_parameters.mcs_index))), # train with all supported MCSs 114 | mcs_training_snr_db_offset=mcs_training_snr_db_offset, 115 | mcs_training_probs=mcs_training_probs, 116 | xla=sys_parameters.xla) 117 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | from .baseline_rx import BaselineReceiver 12 | from .e2e_model import E2E_Model 13 | from .neural_rx import NeuralPUSCHReceiver, NeuralReceiverONNX 14 | from .parameters import Parameters 15 | from .utils import load_weights, training_loop, save_weights, plot_results, plot_gp, export_constellation, sample_along_trajectory, serialize_example 16 | from .channel_models import DoubleTDLChannel, DatasetChannel 17 | from .onnx_utils import DataGeneratorAerial, DataEvaluator, precalculate_nnrx_indices 18 | from .impairments import FrequencyOffset 19 | -------------------------------------------------------------------------------- /utils/baseline_rx.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | # Implements baseline receiver algorithms for performance evaluation 12 | 13 | from tensorflow.keras.layers import Layer 14 | import tensorflow as tf 15 | from itertools import combinations 16 | import numpy as np 17 | 18 | from sionna.ofdm import LMMSEInterpolator, KBestDetector, LinearDetector, LSChannelEstimator 19 | from sionna.nr import PUSCHReceiver, TBDecoder, PUSCHTransmitter, PUSCHLSChannelEstimator 20 | from sionna.utils import flatten_last_dims, split_dim, flatten_dims 21 | 22 | 23 | class BaselineReceiver(Layer): 24 | """BaselineReceiver class implementing a Sionna baseline receiver for 25 | different receiver architectures. 26 | 27 | Parameters 28 | ---------- 29 | sys_parameters : Parameters 30 | The system parameters. 31 | 32 | dtype : tf.complex64, optional 33 | The datatype of the layer, by default tf.complex64. 34 | 35 | return_tb_status : bool, optional 36 | Whether to return transport block status, by default False. 37 | 38 | Input 39 | ----- 40 | inputs : list 41 | [y, no] or [y, h, no] (only for 'baseline_perf_csi') 42 | 43 | y : [batch_size, num_subcarriers, num_ofdm_symbols, num_rx_ant], tf.complex64 44 | The received OFDM resource grid after cyclic prefix removal and FFT. 45 | 46 | no : tf.float32 47 | Noise variance. Must have broadcastable shape to ``y``. 48 | 49 | h : [batch_size, num_subcarriers, num_ofdm_symbols, num_rx_ant], tf.complex64 50 | Channel frequency responses. Only required for for 51 | 'baseline_perf_csi'. 52 | 53 | Output 54 | ------ 55 | b_hat : [batch_size, num_tx, tb_size], tf.float32 56 | The reconstructed payload bits of each transport block. 57 | 58 | tb_crc_status : [batch_size, num_tx], tf.bool 59 | Transport block CRC status. Only returned if `return_tb_status` 60 | is `True`. 61 | """ 62 | 63 | def __init__(self, 64 | sys_parameters, 65 | dtype=tf.complex64, 66 | return_tb_status=False, 67 | mcs_arr_eval_idx=0, 68 | **kwargs): 69 | 70 | super().__init__(dtype=dtype, **kwargs) 71 | self._sys_parameters = sys_parameters 72 | self._return_tb_status = return_tb_status 73 | 74 | ################################### 75 | # Channel Estimation 76 | ################################### 77 | if sys_parameters.system in ('baseline_lmmse_kbest', 78 | 'baseline_lmmse_lmmse'): 79 | # Setup channel estimator for non-perfect CSI 80 | 81 | # Use low-complexity LMMSE interpolator for large bandwidth parts 82 | # to keep computational complexity feasible. 83 | # Remark: dimensions are hard-coded in config. Needs to be adjusted 84 | # for different PRB dimensions. 85 | if sys_parameters.n_size_bwp > 100: 86 | print("Applying low complexity LMMSE interpolation with " \ 87 | "reduced number of PRBs.") 88 | 89 | # use automatic mode to find suitable split parameters 90 | if sys_parameters.lmmse_num_prbs==-1: 91 | print("Using automatic LMMSE splitting.") 92 | # find prime factorial of num_prbs 93 | n = sys_parameters.n_size_bwp 94 | prime_factors = [] 95 | i = 2 96 | x = n 97 | while i 20 and product < best_product: 112 | best_product = product 113 | reduction = sys_parameters.n_size_bwp / best_product 114 | print(f"Using reduction factor of {reduction}") 115 | else: 116 | reduction = sys_parameters.n_size_bwp \ 117 | / sys_parameters.lmmse_num_prbs 118 | 119 | if int(reduction)!=reduction: 120 | raise ValueError("n_size_bwp must be multiple of " \ 121 | "lmmse_num_prbs.") 122 | reduction = int(reduction) 123 | 124 | # modify PUSCH configs for reduced n_size_bwp 125 | pcs = [] 126 | for i in range(0, len(sys_parameters.pusch_configs[mcs_arr_eval_idx])): 127 | pc = sys_parameters.pusch_configs[mcs_arr_eval_idx][i].clone() 128 | pc.carrier.n_size_grid = int(sys_parameters.n_size_bwp // reduction) 129 | pcs.append(pc) 130 | 131 | self._pusch_transmitter_small = PUSCHTransmitter(pcs) 132 | resource_grid = self._pusch_transmitter_small.resource_grid 133 | pilot_pattern = resource_grid.pilot_pattern 134 | cov_mat_time = sys_parameters.time_cov_mat 135 | offset = 0 136 | cov_mat_freq = sys_parameters.freq_cov_mat[ 137 | offset:(resource_grid.fft_size + offset), 138 | offset:(resource_grid.fft_size + offset) 139 | ] 140 | cov_mat_space = sys_parameters.space_cov_mat 141 | 142 | interpolator = LMMSEInterpolator( 143 | pilot_pattern, 144 | cov_mat_time=cov_mat_time, 145 | cov_mat_freq=cov_mat_freq, 146 | cov_mat_space=cov_mat_space, 147 | order="s-f-t" 148 | ) 149 | # 5G PUSCH version of low-complexity LMMSE 150 | self._est = LowComplexityPUSCHLMSEEstimator( 151 | resource_grid=sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 152 | dmrs_length=pc.dmrs.length, 153 | dmrs_additional_position=pc.dmrs.additional_position, 154 | num_cdm_groups_without_data=\ 155 | pc.dmrs.num_cdm_groups_without_data, 156 | interpolator=interpolator, 157 | sys_parameters=sys_parameters, 158 | reduction=reduction 159 | ) 160 | else: 161 | # Use standard Sionna LMMSE interpolator over all PRBs 162 | interpolator = LMMSEInterpolator( 163 | sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid.pilot_pattern, 164 | cov_mat_time=sys_parameters.time_cov_mat, 165 | cov_mat_freq=sys_parameters.freq_cov_mat, 166 | cov_mat_space=sys_parameters.space_cov_mat, 167 | order="s-f-t" 168 | ) 169 | pc = sys_parameters.pusch_configs[mcs_arr_eval_idx][0] 170 | self._est = PUSCHLSChannelEstimator( 171 | resource_grid=sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 172 | dmrs_length=pc.dmrs.length, 173 | dmrs_additional_position=pc.dmrs.additional_position, 174 | num_cdm_groups_without_data=\ 175 | pc.dmrs.num_cdm_groups_without_data, 176 | interpolator=interpolator 177 | ) 178 | elif sys_parameters.system in ('baseline_lsnn_lmmse'): 179 | pc = sys_parameters.pusch_configs[mcs_arr_eval_idx][0] 180 | self._est = PUSCHLSChannelEstimator( 181 | resource_grid=sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 182 | dmrs_length=pc.dmrs.length, 183 | dmrs_additional_position=pc.dmrs.additional_position, 184 | num_cdm_groups_without_data=pc.dmrs.num_cdm_groups_without_data, 185 | interpolation_type="nn" 186 | ) 187 | elif sys_parameters.system in ('baseline_lslin_lmmse'): 188 | pc = sys_parameters.pusch_configs[mcs_arr_eval_idx][0] 189 | self._est = PUSCHLSChannelEstimator( 190 | resource_grid=sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 191 | dmrs_length=pc.dmrs.length, 192 | dmrs_additional_position=pc.dmrs.additional_position, 193 | num_cdm_groups_without_data=pc.dmrs.num_cdm_groups_without_data, 194 | interpolation_type="lin" 195 | ) 196 | #self._est = LSChannelEstimator( 197 | # resource_grid=sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 198 | # interpolation_type="lin") 199 | elif sys_parameters.system in ('baseline_perf_csi_lmmse', 200 | 'baseline_perf_csi_kbest'): 201 | self._est = "perfect" 202 | 203 | ################################### 204 | # Detection 205 | ################################### 206 | if sys_parameters.system in ('baseline_lmmse_kbest', 207 | 'baseline_perf_csi_kbest'): 208 | # Init K-best detector 209 | self._detector = KBestDetector( 210 | "bit", 211 | sys_parameters.max_num_tx, 212 | 64, 213 | sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 214 | sys_parameters.sm, 215 | constellation_type="qam", 216 | num_bits_per_symbol=\ 217 | sys_parameters.transmitters[mcs_arr_eval_idx]._num_bits_per_symbol 218 | ) 219 | elif sys_parameters.system in ('baseline_lmmse_lmmse', 220 | 'baseline_lsnn_lmmse', 221 | 'baseline_lslin_lmmse', 222 | 'baseline_perf_csi_lmmse'): 223 | # Init LMMSE detector 224 | self._detector = LinearDetector( 225 | "lmmse", 226 | "bit", 227 | sys_parameters.demapping_type, 228 | sys_parameters.transmitters[mcs_arr_eval_idx]._resource_grid, 229 | sys_parameters.sm, 230 | constellation_type="qam", 231 | num_bits_per_symbol=\ 232 | sys_parameters.transmitters[mcs_arr_eval_idx]._num_bits_per_symbol 233 | ) 234 | 235 | ################################### 236 | # Decoding 237 | ################################### 238 | self._decoder = TBDecoder( 239 | sys_parameters.transmitters[mcs_arr_eval_idx]._tb_encoder, 240 | num_bp_iter=sys_parameters.num_bp_iter, 241 | cn_type=sys_parameters.cn_type 242 | ) 243 | 244 | self._receiver = PUSCHReceiver( 245 | sys_parameters.transmitters[mcs_arr_eval_idx], 246 | channel_estimator=self._est, 247 | mimo_detector=self._detector, 248 | tb_decoder=self._decoder, 249 | stream_management=None, # Will be derived from transmitters 250 | input_domain="freq", 251 | return_tb_crc_status=self._return_tb_status 252 | ) 253 | 254 | def call(self, inputs): 255 | if self._sys_parameters.system in ("baseline_perf_csi_kbest", 256 | "baseline_perf_csi_lmmse"): 257 | y, h, no = inputs 258 | b_hat = self._receiver([y, h, no]) 259 | else: 260 | y, no = inputs 261 | b_hat = self._receiver([y, no]) 262 | return b_hat 263 | 264 | # The following LMMSE estimator implementations are used to keep the 265 | # complexity of the LMMSEEstimator class feasible. 266 | 267 | class LowComplexityLMSEEstimator(LSChannelEstimator): 268 | """LowComplexityLMSEEstimator class for scalable LMMSE estimation for a 269 | large number of PRBs. 270 | 271 | The LMMSE estimation is only applied to a smaller number of PRBs 272 | instead of the entire number of PRBs. This leads to a small performance 273 | degradation, but keeps the computational (and memory) complexity 274 | significantly lower. 275 | Please note that these blocks are experimental e.g., the batch-size is 276 | hard-coded for XLA support (derived from sys_parameters); the same holds for 277 | the num_rx parameter and the number of streams per tx. 278 | 279 | Input 280 | ----- 281 | inputs : list 282 | [y, no] 283 | 284 | y : [batch_size, num_subcarriers, num_ofdm_symbols, num_rx_ant], tf.complex64 285 | The received OFDM resource grid after cyclic prefix removal and FFT. 286 | 287 | no : tf.float32 288 | Noise variance. Must have broadcastable shape to ``y``. 289 | 290 | Output 291 | ------ 292 | 293 | h_hat : [batch_size, 1, num_rx_ant, num_tx, 1, num_ofdm_symbols, fft_size], tf.complex 294 | Channel estimates across the entire resource grid for all 295 | transmitters and streams 296 | 297 | err_var : Same shape as ``h_hat``, tf.float 298 | Channel estimation error variances across the entire resource grid 299 | for all transmitters and streams 300 | """ 301 | 302 | 303 | def __init__(self, resource_grid, interpolator, 304 | sys_parameters, reduction=4): 305 | super().__init__(resource_grid, interpolator=interpolator, 306 | dtype=tf.complex64) 307 | self._reduction = reduction 308 | self._sys_parameters = sys_parameters 309 | 310 | # static shapes 311 | self._num_pilots = self._sys_parameters.transmitters[0]._resource_grid.num_pilot_symbols.numpy() 312 | 313 | def call(self, inputs): 314 | y, no = inputs 315 | y_eff = self._removed_nulled_scs(y) 316 | y_eff_flat = flatten_last_dims(y_eff) 317 | y_pilots = tf.gather(y_eff_flat, self._pilot_ind, axis=-1) 318 | h_hat, err_var = self.estimate_at_pilot_locations(y_pilots, no) 319 | err_var = tf.broadcast_to(err_var, tf.shape(h_hat)) 320 | 321 | # Hard-coded batch size 322 | s = [self._sys_parameters.batch_size_eval_small] 323 | s.append(1) # num_rx 324 | s.append(self._sys_parameters.num_rx_antennas) 325 | s.append(self._sys_parameters.max_num_tx) 326 | s.append(1) # num_layer 327 | s.append(self._num_pilots) 328 | 329 | h_hat = tf.ensure_shape(h_hat, shape=s) 330 | err_var = tf.ensure_shape(err_var, shape=s) 331 | 332 | h_hat2 = split_dim(h_hat, [2, -1], axis=tf.rank(h_hat) - 1) 333 | h_hat3 = split_dim(h_hat2, [self._reduction, -1], 334 | axis=tf.rank(h_hat2) - 1) 335 | h_hat4 = tf.transpose(h_hat3, perm=[6, 0, 1, 2, 3, 4, 5, 7]) 336 | h_hat5 = flatten_last_dims(h_hat4, 2) 337 | h_hat6 = flatten_dims(h_hat5, 2, 0) 338 | 339 | err_var2 = split_dim(err_var, [2, -1], axis=tf.rank(err_var) - 1) 340 | err_var3 = split_dim(err_var2, [self._reduction, -1], 341 | axis=tf.rank(err_var2) - 1) 342 | err_var4 = tf.transpose(err_var3, perm=[6, 0, 1, 2, 3, 4, 5, 7]) 343 | err_var5 = flatten_last_dims(err_var4, 2) 344 | err_var6 = flatten_dims(err_var5, 2, 0) 345 | 346 | h_hat7, err_var7 = self._interpol(h_hat6, err_var6) 347 | err_var7 = tf.maximum(err_var7, tf.cast(0, err_var7.dtype)) 348 | 349 | h_hat8 = split_dim(h_hat7, [self._reduction, -1], 0) 350 | h_hat9 = tf.transpose(h_hat8, perm=[1, 2, 3, 4, 5, 6, 0, 7]) 351 | h_hat10 = flatten_last_dims(h_hat9, 2) 352 | 353 | err_var8 = split_dim(err_var7, [self._reduction, -1], 0) 354 | err_var9 = tf.transpose(err_var8, perm=[1, 2, 3, 4, 5, 6, 0, 7]) 355 | err_var10 = flatten_last_dims(err_var9, 2) 356 | 357 | return h_hat10, err_var10 358 | 359 | class LowComplexityPUSCHLMSEEstimator(PUSCHLSChannelEstimator): 360 | """LowComplexityPUSCHLMSEEstimator class for scalable LMMSE estimation for 5G PUSCH. 361 | 362 | The LMMSE estimation is only applied to a smaller number of PRBs 363 | instead of the entire number of PRBs. This leads to a small performance 364 | degradation, but keeps the computational (and memory) complexity 365 | significantly lower. 366 | Please note that these blocks are experimental e.g., the batch-size is 367 | hard-coded for XLA support (derived from sys_parameters); the same holds for 368 | the num_rx parameter and the number of streams per tx. 369 | 370 | Remark: Similar to LowComplexityLMMSEEstimator, but supports 371 | FOCC, i.e., non-orthogonal DMRS as done in some 5G NR configurations. 372 | 373 | Input 374 | ----- 375 | inputs : list 376 | [y, no] 377 | 378 | y : [batch_size, num_subcarriers, num_ofdm_symbols, num_rx_ant], tf.complex64 379 | The received OFDM resource grid after cyclic prefix removal and FFT. 380 | 381 | no : tf.float32 382 | Noise variance. Must have broadcastable shape to ``y``. 383 | 384 | Output 385 | ------ 386 | 387 | h_hat : [batch_size, 1, num_rx_ant, num_tx, 1, num_ofdm_symbols, fft_size], tf.complex 388 | Channel estimates across the entire resource grid for all 389 | transmitters and streams 390 | 391 | err_var : Same shape as ``h_hat``, tf.float 392 | Channel estimation error variances across the entire resource grid 393 | for all transmitters and streams 394 | """ 395 | 396 | def __init__(self, resource_grid, dmrs_length, dmrs_additional_position, 397 | num_cdm_groups_without_data, interpolator, sys_parameters, 398 | reduction=4): 399 | super().__init__(resource_grid=resource_grid, 400 | dmrs_length=dmrs_length, 401 | dmrs_additional_position=dmrs_additional_position, 402 | num_cdm_groups_without_data=num_cdm_groups_without_data, 403 | interpolator=interpolator, 404 | dtype=tf.complex64) 405 | self._reduction = reduction 406 | self._sys_parameters = sys_parameters 407 | 408 | # static shapes 409 | self._num_pilots = self._sys_parameters.transmitters[0]._resource_grid.num_pilot_symbols.numpy() 410 | 411 | def call(self, inputs): 412 | y, no = inputs 413 | y_eff = self._removed_nulled_scs(y) 414 | y_eff_flat = flatten_last_dims(y_eff) 415 | y_pilots = tf.gather(y_eff_flat, self._pilot_ind, axis=-1) 416 | h_hat, err_var = self.estimate_at_pilot_locations(y_pilots, no) 417 | err_var = tf.broadcast_to(err_var, tf.shape(h_hat)) 418 | 419 | # Hard-coded batch size 420 | s = [self._sys_parameters.batch_size_eval_small] 421 | s.append(1) # num_rx 422 | s.append(self._sys_parameters.num_rx_antennas) 423 | s.append(self._sys_parameters.max_num_tx) 424 | s.append(1) # num_layer 425 | s.append(self._num_pilots) 426 | 427 | h_hat = tf.ensure_shape(h_hat, shape=s) 428 | err_var = tf.ensure_shape(err_var, shape=s) 429 | 430 | h_hat2 = split_dim(h_hat, [2, -1], axis=tf.rank(h_hat) - 1) 431 | h_hat3 = split_dim(h_hat2, [self._reduction, -1], 432 | axis=tf.rank(h_hat2) - 1) 433 | h_hat4 = tf.transpose(h_hat3, perm=[6, 0, 1, 2, 3, 4, 5, 7]) 434 | h_hat5 = flatten_last_dims(h_hat4, 2) 435 | h_hat6 = flatten_dims(h_hat5, 2, 0) 436 | 437 | err_var2 = split_dim(err_var, [2, -1], axis=tf.rank(err_var) - 1) 438 | err_var3 = split_dim(err_var2, [self._reduction, -1], 439 | axis=tf.rank(err_var2) - 1) 440 | err_var4 = tf.transpose(err_var3, perm=[6, 0, 1, 2, 3, 4, 5, 7]) 441 | err_var5 = flatten_last_dims(err_var4, 2) 442 | err_var6 = flatten_dims(err_var5, 2, 0) 443 | 444 | h_hat7, err_var7 = self._interpol(h_hat6, err_var6) 445 | err_var7 = tf.maximum(err_var7, tf.cast(0, err_var7.dtype)) 446 | 447 | h_hat8 = split_dim(h_hat7, [self._reduction, -1], 0) 448 | h_hat9 = tf.transpose(h_hat8, perm=[1, 2, 3, 4, 5, 6, 0, 7]) 449 | h_hat10 = flatten_last_dims(h_hat9, 2) 450 | 451 | err_var8 = split_dim(err_var7, [self._reduction, -1], 0) 452 | err_var9 = tf.transpose(err_var8, perm=[1, 2, 3, 4, 5, 6, 0, 7]) 453 | err_var10 = flatten_last_dims(err_var9, 2) 454 | 455 | return h_hat10, err_var10 456 | -------------------------------------------------------------------------------- /utils/channel_models.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | # Implements different channel models for performance evaluation 12 | 13 | from tensorflow.keras.layers import Layer 14 | import tensorflow as tf 15 | import numpy as np 16 | import sionna 17 | from sionna.channel import GenerateOFDMChannel, ApplyOFDMChannel, ChannelModel 18 | from sionna.channel.tr38901 import TDL 19 | 20 | def gnb_correlation_matrix(num_ant, alpha): 21 | assert num_ant in [1,2,4,8] 22 | if num_ant==1: 23 | exponents = np.array([0]) 24 | elif num_ant==2: 25 | exponents = np.array([0, 1]) 26 | elif num_ant==4: 27 | exponents = np.array([0, 1/9, 4/9, 1]) 28 | elif num_ant==8: 29 | exponents = np.array([0, 1/49, 4/49, 9/49, 16/49, 25/49, 36/49, 1]) 30 | row = alpha**exponents 31 | col = np.conj(row) 32 | r = tf.linalg.LinearOperatorToeplitz(col, row) 33 | return tf.cast(r.to_dense(), tf.complex64) 34 | 35 | def ue_correlation_matrix(num_ant, beta): 36 | assert num_ant in [1,2,4] 37 | return gnb_correlation_matrix(num_ant, beta) 38 | 39 | class DoubleTDLChannel(tf.keras.layers.Layer): 40 | """ 41 | Channel model that stacks a 3GPP TDL-B100-400 and TDL-C-300-100 channel 42 | model. This allows to benchmark a two user system in a 3GPP compliant 43 | scenario. 44 | 45 | Parameters 46 | --------- 47 | carrier_frequency: float 48 | Carrier frequency of the simulation. 49 | 50 | resource_grid: ResourceGrid 51 | Resource grid used for the simulation. 52 | 53 | num_rx_ant: int 54 | Number of receiver antennas. 55 | 56 | num_tx_ant: int 57 | Number of transmit antennas for each user. 58 | 59 | norm_channel: bool 60 | If True, the channel is normalized. 61 | 62 | correlation: "low" | "medium" | "high" 63 | Antenna correlation according to 38.901. 64 | 65 | Input 66 | ----- 67 | 68 | (x, no) or x: 69 | Tuple or Tensor: 70 | 71 | x : [batch size, num_tx, num_tx_ant, num_ofdm_symbols, fft_size], 72 | tf.complex 73 | Channel inputs 74 | 75 | no : Scalar or Tensor, tf.float 76 | Scalar or tensor whose shape can be broadcast to the shape of the 77 | channel outputs 78 | 79 | Output 80 | ------- 81 | y : [batch size, num_rx, num_rx_ant, num_ofdm_symbols, fft_size], tf.complex 82 | Channel outputs 83 | h_freq : [batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, 84 | num_ofdm_symbols, fft_size], tf.complex 85 | Channel frequency responses. 86 | """ 87 | def __init__(self, 88 | carrier_frequency, 89 | resource_grid, 90 | num_rx_ant=4, 91 | num_tx_ant=2, 92 | norm_channel=False, 93 | correlation="low"): 94 | super().__init__() 95 | 96 | assert correlation in ["low", "medium", "high"] 97 | 98 | print(f"Loading DoubleTDL with {correlation} correlation.") 99 | 100 | if correlation=="low": 101 | alpha = beta = 0 102 | elif correlation=="medium": 103 | alpha = 0.9 104 | beta = 0.3 105 | else: 106 | alpha = 0.9 107 | beta = 0.9 108 | 109 | tx_corr_mat = ue_correlation_matrix(num_tx_ant, beta) 110 | rx_corr_mat = gnb_correlation_matrix(num_rx_ant, alpha) 111 | 112 | # TDL B100 model 113 | delay_spread_1 = 100e-9 114 | doppler_spread_1 = 400 115 | speed_1 = doppler_spread_1 * sionna.SPEED_OF_LIGHT / carrier_frequency 116 | tdl1 = TDL("B100", 117 | delay_spread_1, 118 | carrier_frequency, 119 | max_speed=speed_1, 120 | num_tx_ant=num_tx_ant, 121 | num_rx_ant=num_rx_ant, 122 | rx_corr_mat=rx_corr_mat, 123 | tx_corr_mat=tx_corr_mat) 124 | 125 | # TDL C300 model 126 | delay_spread_2 = 300e-9 127 | doppler_spread_2 = 100 128 | speed_2 = doppler_spread_2 * sionna.SPEED_OF_LIGHT / carrier_frequency 129 | tdl2 = TDL("C300", 130 | delay_spread_2, 131 | carrier_frequency, 132 | max_speed=speed_2, 133 | num_tx_ant=num_tx_ant, 134 | num_rx_ant=num_rx_ant, 135 | rx_corr_mat=rx_corr_mat, 136 | tx_corr_mat=tx_corr_mat) 137 | 138 | self._gen_channel_1 = GenerateOFDMChannel( 139 | tdl1, 140 | resource_grid, 141 | normalize_channel=norm_channel) 142 | 143 | self._gen_channel_2 = GenerateOFDMChannel( 144 | tdl2, 145 | resource_grid, 146 | normalize_channel=norm_channel) 147 | 148 | self._apply_channel = ApplyOFDMChannel() 149 | 150 | def call(self, inputs): 151 | 152 | x, no = inputs 153 | batch_size = tf.shape(x)[0] 154 | h1 = self._gen_channel_1(batch_size) 155 | h2 = self._gen_channel_2(batch_size) 156 | 157 | # stack the two models 158 | h = tf.concat([h1, h2], axis=3) 159 | 160 | y = self._apply_channel([x, h, no]) 161 | return y, h 162 | 163 | class DatasetChannel(ChannelModel): 164 | """Channel model from a TFRecords Dataset File 165 | The entire dataset is read in memory. 166 | 167 | This version supports XLA acceleration. 168 | 169 | 170 | Parameter 171 | --------- 172 | tfrecord_filename: str 173 | Filename of the pre-computed dataset. 174 | 175 | max_num_examples: int 176 | Max number of samples loaded from dataset. If equals to "-1" 177 | the entire dataset will be loaded. Defines memory occupation. 178 | 179 | Input 180 | ----- 181 | batchsize: int 182 | How many samples shall be returned. 183 | 184 | Output 185 | ------ 186 | a: [batch_size,...] 187 | batch_size samples from ``a``. Exact shape depends on dataset. 188 | 189 | tau: [batch_size,...] 190 | batch_size samples from ``tau``. Exact shape depends on dataset. 191 | 192 | """ 193 | def __init__(self, tfrecord_filename, max_num_examples=-1, training=True, 194 | num_tx=1, random_subsampling=True): 195 | 196 | self._training = training 197 | self._num_tx = num_tx 198 | self._random_subsampling = random_subsampling 199 | 200 | # Read raw dataset 201 | dataset = tf.data.TFRecordDataset([tfrecord_filename]) \ 202 | .map(self._parse_function, 203 | num_parallel_calls=tf.data.AUTOTUNE) \ 204 | .take(max_num_examples) \ 205 | .batch(1024) 206 | 207 | # Load entire dataset into memory as large tensor 208 | a = None 209 | tau = None 210 | for example in dataset: 211 | # aggregate all channels in batch direction to multiple users. 212 | # i.e., move batch direction to num_tx direction. 213 | # 214 | # Evaluation data set already has two active users for each batch 215 | # sample. 216 | # Thus, every other sample after the aggregation belong to the same 217 | # user. 218 | a_ex, tau_ex = example 219 | a_ex = tf.split(a_ex, a_ex.shape[0], axis=0) 220 | a_ex = tf.concat(a_ex, axis=3) 221 | tau_ex = tf.split(tau_ex, tau_ex.shape[0], axis=0) 222 | tau_ex = tf.concat(tau_ex, axis=2) 223 | if a is None: 224 | a = a_ex 225 | tau = tau_ex 226 | else: 227 | a = tf.concat([a, a_ex], axis=3) 228 | tau = tf.concat([tau, tau_ex], axis=2) 229 | 230 | if training: 231 | # User positions are randomly sampled. In order to avoid sampling 232 | # the same positions multiple times within one batch sample, we 233 | # split the dataset into equal parts for each user to sample from 234 | # during simulations. 235 | num_examples = int(a.shape[3]/self._num_tx) 236 | self._num_examples = num_examples 237 | self._a = [] 238 | self._tau = [] 239 | for i in range(self._num_tx): 240 | self._a.append(a[:,:,:,i*num_examples:(i+1)*num_examples]) 241 | self._tau.append(tau[:,:,i*num_examples:(i+1)*num_examples]) 242 | else: 243 | self._num_examples = a.shape[3] 244 | self._a = [a,] 245 | self._tau = [tau,] 246 | 247 | @staticmethod 248 | def _parse_function(proto): 249 | description = { 250 | 'a': tf.io.FixedLenFeature([], tf.string), 251 | 'tau': tf.io.FixedLenFeature([], tf.string), 252 | } 253 | features = tf.io.parse_single_example(proto, description) 254 | a = tf.io.parse_tensor(features['a'], out_type=tf.complex64) 255 | tau = tf.io.parse_tensor(features['tau'], out_type=tf.float32) 256 | # tf.print(tf.shape(a)) 257 | return a, tau 258 | 259 | 260 | def __call__(self, batch_size=None, 261 | num_time_steps=None, 262 | sampling_frequency=None): 263 | # default values are used for compatibility with other TF functions. 264 | 265 | # Remark: this is random subsampling 266 | # random sampling is also done in eval mode; keep in mind that even 267 | # though UE is on trajectory, we need many slot realizations for good 268 | # BLER curves (in any case we sample new AWGN noise) 269 | 270 | a = None 271 | tau = None 272 | 273 | if self._training: 274 | if not self._random_subsampling: 275 | ind = tf.random.uniform([batch_size], 276 | maxval=self._num_examples, dtype=tf.int32) 277 | # randomly subsample from different subsets 278 | for ue_idx in range(self._num_tx): 279 | if self._random_subsampling: 280 | ind = tf.random.uniform( 281 | [batch_size], 282 | maxval=self._num_examples, 283 | dtype=tf.int32) 284 | 285 | # Gather reshape and combine 286 | a_ = tf.gather(self._a[ue_idx], ind, axis=3) 287 | a_ = tf.transpose(a_, perm=[3, 1, 2, 0, 4, 5, 6]) 288 | tau_ = tf.gather(self._tau[ue_idx], ind, axis=2) 289 | tau_ = tf.transpose(tau_, perm=[2, 1, 0, 3]) 290 | if a is not None: 291 | a = tf.concat([a, a_], axis=3) 292 | tau = tf.concat([tau, tau_], axis=2) 293 | else: 294 | a = a_ 295 | tau = tau_ 296 | else: 297 | # samples in self._a alternating between both trajectories 298 | if not self._random_subsampling: 299 | # no random sub-sampling: take subsequent two samples 300 | ind = tf.random.uniform([batch_size], 301 | maxval=self._num_examples//self._num_tx, 302 | dtype=tf.int32) 303 | ind = tf.repeat(tf.expand_dims(ind, axis=-1), 304 | repeats=self._num_tx, axis=-1) 305 | else: 306 | ind = tf.random.uniform([batch_size, self._num_tx], 307 | maxval=self._num_examples//self._num_tx, 308 | dtype=tf.int32) 309 | # sample subsequent points from all ues 310 | ind = self._num_tx * ind + tf.expand_dims( 311 | tf.range(self._num_tx, dtype=tf.int32), 312 | axis=0) 313 | 314 | a = tf.transpose( 315 | tf.squeeze(tf.gather(self._a[0], ind, axis=3), axis=0), 316 | perm=[2,0,1,3,4,5,6]) 317 | tau = tf.transpose( 318 | tf.squeeze(tf.gather(self._tau[0], ind, axis=2), axis=0), 319 | perm=[1,0,2,3]) 320 | 321 | return a, tau 322 | -------------------------------------------------------------------------------- /utils/impairments.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | ##### Hardware impairment layers 12 | 13 | from tensorflow.keras.layers import Layer 14 | import tensorflow as tf 15 | from sionna.ofdm import OFDMModulator, OFDMDemodulator 16 | from sionna.constants import PI 17 | 18 | class FrequencyOffset(Layer): 19 | """ 20 | FrequencyOffset(max_rel_offset, input_domain, resource_grid=None, constant_offset=False, **kwargs) 21 | 22 | Applies a random frequency offset to the input signal. 23 | 24 | Parameters 25 | ---------- 26 | max_rel_offset: float 27 | Maximum absolute frequency offset relative to the sampling rate. 28 | 29 | input_domain: ["time", "freq"], str 30 | Domain of the input signal. It can be either "time" or "freq". 31 | 32 | resource_grid: object, optional 33 | Instance of resource grid. Only used if `input_domain` is "freq". 34 | Defaults to `None`. 35 | 36 | constant_offset: bool, optional 37 | If True, the frequency offset is kept constant. Otherwise, 38 | random frequency offsets are uniformly sampled in the range of 39 | `[-max_rel_offset, max_rel_offset]`. Defaults to `False`. 40 | 41 | Input 42 | ----- 43 | x: [batch_size, num_tx, num_tx_ant, num_time_samples] or [batch_size, 44 | num_tx, num_tx_ant, num_ofdm_symbols, fft_size], tf.complex 45 | The signal to which the frequency offset should be applied. 46 | 47 | Output 48 | ------ 49 | y: [batch_size, num_tx, num_tx_ant, num_time_samples], tf.complex 50 | The signal with the frequency offset applied. 51 | """ 52 | 53 | def __init__(self, max_rel_offset, input_domain, resource_grid=None, 54 | constant_offset=False, **kwargs): 55 | super().__init__(**kwargs) 56 | self._max_rel_offset = tf.cast(max_rel_offset, tf.float32) 57 | 58 | # Determine the range for random uniform offsets 59 | if constant_offset: 60 | self._min_rel_offset = self._max_rel_offset 61 | else: 62 | self._min_rel_offset = -self._max_rel_offset 63 | 64 | self._input_domain = input_domain 65 | self._resource_grid = resource_grid 66 | 67 | # Initialize OFDM modulator and demodulator if the input domain is 68 | # "freq" 69 | if self._input_domain == "freq": 70 | assert self._resource_grid is not None, \ 71 | "resource_grid must be provided when input_domain is 'freq'." 72 | self._modulator = OFDMModulator(resource_grid.cyclic_prefix_length) 73 | self._demodulator = OFDMDemodulator( 74 | resource_grid.fft_size, 0, 75 | resource_grid.cyclic_prefix_length) 76 | 77 | def call(self, inputs): 78 | # If input domain is "freq", convert inputs to time domain 79 | # and apply CFO in time domain and convert back afterwards 80 | if self._input_domain == "freq": 81 | inputs = self._modulator(inputs) 82 | 83 | # Determine the number of time samples 84 | num_time_samples = tf.shape(inputs)[-1] 85 | 86 | # Create shape tensor for frequency offsets 87 | s = tf.concat((tf.shape(inputs)[0:2], tf.ones((2,), tf.int32)), axis=0) 88 | 89 | # Sample random frequency offsets 90 | fo = tf.random.uniform(s, # Shape: [batch_size, num_tx, 1, 1] 91 | minval=self._min_rel_offset, 92 | maxval=self._max_rel_offset, 93 | dtype=tf.float32) 94 | 95 | # Calculate phase increments and shifts 96 | phase_increment = fo * 2 * PI 97 | time_steps = tf.reshape( 98 | tf.range(0, num_time_samples, dtype=tf.float32), 99 | [1, 1, 1, -1]) 100 | phase_shifts = time_steps * phase_increment 101 | 102 | # Apply frequency offset to the input signal 103 | exp = tf.cast(tf.exp(tf.complex(0., phase_shifts)), inputs.dtype) 104 | outputs = exp * inputs 105 | 106 | # If input domain was "freq", convert outputs back to frequency domain 107 | if self._input_domain == "freq": 108 | outputs = self._demodulator(outputs) 109 | 110 | return outputs 111 | -------------------------------------------------------------------------------- /utils/parameters.py: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. 2 | # SPDX-License-Identifier: LicenseRef-NvidiaProprietary 3 | # 4 | # NVIDIA CORPORATION, its affiliates and licensors retain all intellectual 5 | # property and proprietary rights in and to this material, related 6 | # documentation and any modifications thereto. Any use, reproduction, 7 | # disclosure or distribution of this material and related documentation 8 | # without an express license agreement from NVIDIA CORPORATION or 9 | # its affiliates is strictly prohibited. 10 | 11 | # reads in the configuration file and initialized all relevant system components 12 | # This allows to train and evaluate different system configurations on the same # server and simplifies logging of the training process. 13 | 14 | import numpy as np 15 | import configparser 16 | import tensorflow as tf 17 | from os.path import exists 18 | from sionna.nr import PUSCHConfig, PUSCHDMRSConfig, TBConfig, CarrierConfig, PUSCHTransmitter, PUSCHPilotPattern 19 | from sionna.channel.tr38901 import PanelArray, UMi, TDL, UMa 20 | from sionna.mimo import StreamManagement 21 | from sionna.channel import OFDMChannel, AWGN 22 | from .channel_models import DoubleTDLChannel, DatasetChannel 23 | from .impairments import FrequencyOffset 24 | 25 | class Parameters: 26 | r""" 27 | Simulation parameters 28 | 29 | Parameters 30 | ---------- 31 | config_name : str 32 | name of the config file. 33 | 34 | system : str 35 | Receiver algorithm to be used.Must be one of: 36 | * "nrx" : Neural receiver 37 | * "baseline_lmmse_kbest" : LMMSE estimation and K-Best detection 38 | * "baseline_perf_csi_kbest" : perfect CSI and K-Best detection 39 | * "baseline_lmmse_lmmse" : LMMSE estimation and LMMSE equalization 40 | * "baseline_lsnn_lmmse" : LS estimation/nn interpolation and LMMSE equalization 41 | * "dummy" : stops after parameter import. Can be used only to parse the 42 | config. 43 | 44 | training: bool, False, 45 | If True, training parameters are loaded. Otherwise, the evaluation 46 | parameters are used. 47 | 48 | verbose: bool, False 49 | If True, additional information is printed during init. 50 | 51 | compute_cov: bool, False 52 | If True, the UMi channel model is loaded automatically to avoid 53 | overfitting to TDL models. 54 | 55 | num_tx_eval: int or None 56 | If provided, the max number of users is limited to ``num_tx_eval``. 57 | For this, the first DMRS ports are selected. 58 | """ 59 | def __init__(self, config_name, system="dummy", training=False, verbose=False, compute_cov=False, num_tx_eval=None): 60 | 61 | # check input for consistency 62 | assert isinstance(verbose, bool), "verbose must be bool." 63 | assert isinstance(training, bool), "training must be bool." 64 | assert isinstance(config_name, str), "config_name must be str." 65 | assert isinstance(system, str), "system must be str." 66 | assert isinstance(compute_cov, bool), "compute_cov must be bool." 67 | 68 | self.system = system 69 | 70 | ################################### 71 | ##### Load configuration file ##### 72 | ################################### 73 | 74 | # create parser object and read config file 75 | fn = f'../config/{config_name}' 76 | if exists(fn): 77 | config = configparser.RawConfigParser() 78 | # automatically add fileformat if needed 79 | config_name.replace(".cfg","") + ".cfg" 80 | config.read(fn) 81 | else: 82 | raise FileNotFoundError("Unknown config file.") 83 | 84 | # and import all parameters as attributes 85 | self.config_str = "" 86 | for section in config.sections(): 87 | s = f"\n---- {section} ----- " 88 | self.config_str += s + "
" # add linebreak for Tensorboard 89 | if verbose: 90 | print(s) 91 | for option in config.options(section): 92 | setattr(self, f"{option}", eval(config.get(section,option))) 93 | s = f"{option}: {eval(config.get(section,option))}" 94 | self.config_str += s + "
" # add linebreak for Tensorboard 95 | if verbose: 96 | print(s) 97 | 98 | # Overwrite channel and PRBs in inference mode with "eval" parameters 99 | # This allows to configure different parameters during training and 100 | # evaluation. 101 | if not training: 102 | self.channel_type = self.channel_type_eval 103 | self.n_size_bwp = self.n_size_bwp_eval 104 | self.max_ut_velocity = self.max_ut_velocity_eval 105 | self.min_ut_velocity = self.min_ut_velocity_eval 106 | self.channel_norm = self.channel_norm_eval 107 | self.cfo_offset_ppm = self.cfo_offset_ppm_eval 108 | self.tfrecord_filename = self.tfrecord_filename_eval 109 | if self.channel_type == "Dataset": 110 | self.random_subsampling = self.random_subsampling_eval 111 | 112 | # load only config parameters and return without initializing the rest 113 | # of the system 114 | if self.system == "dummy": 115 | return 116 | 117 | ##################################### 118 | ##### Init PUSCH configurations ##### 119 | ##################################### 120 | 121 | # init PUSCHConfig 122 | carrier_config = CarrierConfig( 123 | n_cell_id=self.n_cell_id, 124 | cyclic_prefix=self.cyclic_prefix, 125 | subcarrier_spacing=int(self.subcarrier_spacing/1e3), # in kHz 126 | n_size_grid=self.n_size_bwp, 127 | n_start_grid=self.n_start_grid, 128 | slot_number=self.slot_number, 129 | frame_number=self.frame_number) 130 | 131 | # init DMRSConfig 132 | pusch_dmrs_config=PUSCHDMRSConfig( 133 | config_type=self.dmrs_config_type, 134 | type_a_position=self.dmrs_type_a_position, 135 | additional_position=self.dmrs_additional_position, 136 | length=self.dmrs_length, 137 | dmrs_port_set=self.dmrs_port_sets[0], # first user 138 | n_scid=self.n_scid, 139 | num_cdm_groups_without_data=self.num_cdm_groups_without_data) 140 | 141 | mcs_list = self.mcs_index 142 | # generate pusch configs for all MCSs 143 | self.pusch_configs = [] # self.pusch_configs[MCS_CONFIG][N_UE] 144 | for mcs_list_idx in range(len(mcs_list)): 145 | self.pusch_configs.append([]) 146 | mcs_index = mcs_list[mcs_list_idx] 147 | # init TBConfig 148 | tb_config = TBConfig( 149 | mcs_index=mcs_index, 150 | mcs_table=self.mcs_table, 151 | channel_type="PUSCH") 152 | #n_id=self.n_ids[0]) 153 | 154 | # first user PUSCH config 155 | pc = PUSCHConfig( 156 | carrier_config=carrier_config, 157 | pusch_dmrs_config=pusch_dmrs_config, 158 | tb_config=tb_config, 159 | num_antenna_ports = self.num_antenna_ports, 160 | precoding = self.precoding, 161 | symbol_allocation = self.symbol_allocation, 162 | tpmi = self.tpmi, 163 | mapping_type=self.dmrs_mapping_type,) 164 | 165 | # clone new PUSCHConfig for each additional user 166 | for idx,_ in enumerate(self.dmrs_port_sets): 167 | p = pc.clone() # generate new PUSCHConfig 168 | # set user specific parts 169 | p.dmrs.dmrs_port_set = self.dmrs_port_sets[idx] 170 | # The following parameters are derived from default. 171 | # Comment lines if specific configuration is not required. 172 | p.n_id = self.n_ids[idx] 173 | p.dmrs.n_id = self.dmrs_nid[idx] 174 | p.n_rnti = self.n_rntis[idx] 175 | self.pusch_configs[mcs_list_idx].append(p) 176 | 177 | ############################## 178 | ##### Consistency checks ##### 179 | ############################## 180 | 181 | # after training we can only reduce the number of iterations 182 | assert self.num_nrx_iter_eval<=self.num_nrx_iter, \ 183 | "num_nrx_iter_eval must be smaller or equal num_nrx_iter." 184 | 185 | # for the evaluation, only activate num_tx_eval configs 186 | if not training: 187 | # overwrite num_tx_eval if explicitly provided: 188 | if num_tx_eval is not None: 189 | num_tx_eval = num_tx_eval 190 | else: # if not provided use all available port sets 191 | num_tx_eval = len(self.dmrs_port_sets) 192 | self.max_num_tx = num_tx_eval # non-varying users for evaluation 193 | self.min_num_tx = num_tx_eval # non-varying users for evaluation 194 | 195 | for mcs_list_idx in range(len(mcs_list)): 196 | self.pusch_configs[mcs_list_idx] = self.pusch_configs[mcs_list_idx][:self.max_num_tx] 197 | print(f"Evaluating the first {self.max_num_tx} port sets.") 198 | 199 | ################################## 200 | ##### Configure Transmitters ##### 201 | ################################## 202 | 203 | # Generate and store DMRS for all slot numbers 204 | self.pilots = [] 205 | for slot_num in range(carrier_config.num_slots_per_frame): 206 | for pcs in self.pusch_configs: 207 | for pc in pcs: 208 | pc.carrier.slot_number = slot_num 209 | # only generate pilot pattern for first MCS's PUSCH config, as 210 | # pilots are independent from MCS index 211 | pilot_pattern = PUSCHPilotPattern(self.pusch_configs[0]) 212 | self.pilots.append(pilot_pattern.pilots) 213 | self.pilots = tf.stack(self.pilots, axis=0) 214 | self.pilots = tf.constant(self.pilots) 215 | for pcs in self.pusch_configs: 216 | for pc in pcs: 217 | pc.carrier.slot_number = self.slot_number 218 | 219 | # transmitter is a list of PUSCHTransmitters, one for each MCS 220 | self.transmitters = [] 221 | for mcs_list_idx in range(len(mcs_list)): 222 | # and init transmitter 223 | self.transmitters.append( 224 | PUSCHTransmitter( 225 | self.pusch_configs[mcs_list_idx], 226 | return_bits=False, 227 | output_domain="freq", 228 | verbose=self.verbose)) 229 | 230 | # support end-to-end learning / custom constellations 231 | # see https://arxiv.org/pdf/2009.05261 for details 232 | if self.custom_constellation: # trainable constellations 233 | print("Activating trainable custom constellations.") 234 | self.transmitters[mcs_list_idx]._mapper.constellation.trainable = True 235 | # Center constellations. This could be also deactivated for more 236 | # degrees of freedom. 237 | self.transmitters[mcs_list_idx]._mapper.constellation.center = True 238 | 239 | # chest will fail if we use explicit masking of pilots. 240 | if self.mask_pilots and self.initial_chest in ("ls", "nn"): 241 | print("Warning: initial_chest will fail with masked pilots.") 242 | 243 | # StreamManagement required for KBestDetector 244 | self.sm = StreamManagement(np.ones([1, self.max_num_tx], int), 1) 245 | 246 | ############################## 247 | ##### Initialize Channel ##### 248 | ############################## 249 | 250 | # always use UMi to calculate covariance matrix 251 | if compute_cov: 252 | if not self.channel_type in ("UMi", "UMa"): # use UMa if selected 253 | print("Setting channel type to UMi for covariance computation.") 254 | self.channel_type = "UMi" 255 | 256 | # Sanity check 257 | if self.channel_type in ("DoubleTDLlow","DoubleTDLmedium", 258 | "DoubleTDLhigh") and self.max_num_tx==1: 259 | print("Warning: SelectedDoubleTDL model only defined for 2 "\ 260 | "users. Selecting TDL-B100 instead.") 261 | self.channel_type = "TDL-B100" 262 | 263 | # Initialize channel 264 | # Remark: new channel models can be added here 265 | if self.channel_type in ("UMi", "UMa"): 266 | if self.num_rx_antennas==1: # ignore polarization for single antenna 267 | print("Using vertical polarization for single antenna setup.") 268 | num_cols_per_panel = 1 269 | num_rows_per_panel = 1 270 | polarization = "single" 271 | polarization_type = 'V' 272 | else: 273 | # we use a ULA array to be aligned with TDL models 274 | num_cols_per_panel = self.num_rx_antennas//2 275 | num_rows_per_panel = 1 276 | polarization = "dual" 277 | polarization_type = 'cross' 278 | 279 | bs_array = PanelArray(num_rows_per_panel = num_rows_per_panel, 280 | num_cols_per_panel = num_cols_per_panel, 281 | polarization = polarization, 282 | polarization_type = polarization_type, 283 | antenna_pattern = '38.901', 284 | carrier_frequency = self.carrier_frequency) 285 | 286 | ut_array = PanelArray(num_rows_per_panel = 1, 287 | num_cols_per_panel = pc.num_antenna_ports, 288 | polarization = 'single', 289 | polarization_type = 'V', 290 | antenna_pattern = 'omni', 291 | carrier_frequency = self.carrier_frequency) 292 | 293 | if self.channel_type == "UMi": 294 | self.channel_model = UMi( 295 | carrier_frequency=self.carrier_frequency, 296 | o2i_model = 'low', 297 | bs_array = bs_array, 298 | ut_array = ut_array, 299 | direction = 'uplink', 300 | enable_pathloss = False, 301 | enable_shadow_fading = False) 302 | else: # UMa 303 | self.channel_model = UMa( 304 | carrier_frequency=self.carrier_frequency, 305 | o2i_model = 'low', 306 | bs_array = bs_array, 307 | ut_array = ut_array, 308 | direction = 'uplink', 309 | enable_pathloss = False, 310 | enable_shadow_fading = False) 311 | 312 | self.channel = OFDMChannel( 313 | channel_model=self.channel_model, 314 | resource_grid=self.transmitters[0]._resource_grid, # resource grid is independent of MCS 315 | add_awgn=True, 316 | normalize_channel=self.channel_norm, 317 | return_channel=True) 318 | 319 | elif self.channel_type == "TDL-B100": 320 | tdl = TDL(model="B100", 321 | delay_spread=100e-9, 322 | carrier_frequency=self.carrier_frequency, 323 | min_speed=self.min_ut_velocity, 324 | max_speed=self.max_ut_velocity, 325 | num_tx_ant=pc.num_antenna_ports, 326 | num_rx_ant=self.num_rx_antennas) 327 | self.channel = OFDMChannel(tdl, 328 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 329 | add_awgn=True, 330 | normalize_channel=self.channel_norm, 331 | return_channel=True) 332 | elif self.channel_type == "TDL-C300": 333 | tdl = TDL(model="C300", 334 | delay_spread=300e-9, 335 | carrier_frequency=self.carrier_frequency, 336 | min_speed=self.min_ut_velocity, 337 | max_speed=self.max_ut_velocity, 338 | num_tx_ant=pc.num_antenna_ports, 339 | num_rx_ant=self.num_rx_antennas) 340 | self.channel = OFDMChannel(tdl, 341 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 342 | add_awgn=True, 343 | normalize_channel=self.channel_norm, 344 | return_channel=True) 345 | # DoubleTDL for evaluation 346 | elif self.channel_type == "DoubleTDLlow": 347 | self.channel = DoubleTDLChannel(self.carrier_frequency, 348 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 349 | correlation="low", 350 | num_tx_ant=pc.num_antenna_ports, 351 | norm_channel=self.channel_norm) 352 | # DoubleTDL for evaluation 353 | elif self.channel_type == "DoubleTDLmedium": 354 | self.channel= DoubleTDLChannel(self.carrier_frequency, 355 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 356 | correlation="medium", 357 | num_tx_ant=pc.num_antenna_ports, 358 | norm_channel=self.channel_norm) 359 | # DoubleTDL for evaluation 360 | elif self.channel_type == "DoubleTDLhigh": 361 | self.channel = DoubleTDLChannel(self.carrier_frequency, 362 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 363 | correlation="high", 364 | num_tx_ant=pc.num_antenna_ports, 365 | norm_channel=self.channel_norm) 366 | 367 | elif self.channel_type == "AWGN": 368 | self.channel = AWGN() 369 | 370 | 371 | elif self.channel_type == "Dataset": 372 | channel_model = DatasetChannel("../data/" + self.tfrecord_filename, 373 | max_num_examples=-1, # loads entire dataset 374 | training=training, 375 | num_tx=self.max_num_tx, 376 | random_subsampling=self.random_subsampling, 377 | ) 378 | self.channel = OFDMChannel(channel_model, 379 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 380 | add_awgn=True, 381 | normalize_channel=self.channel_norm, 382 | return_channel=True) 383 | 384 | else: 385 | raise ValueError("Unknown Channel type.") 386 | 387 | # Hardware impairments 388 | if self.cfo_offset_ppm>0: 389 | offset = self.carrier_frequency / 1e6 * self.cfo_offset_ppm 390 | max_rel_offset = offset/self.transmitters[0].resource_grid.bandwidth # resource grid and bandwidth is independent of MCS 391 | self.frequency_offset = FrequencyOffset( 392 | max_rel_offset, 393 | "freq", 394 | self.transmitters[0].resource_grid, # resource grid is independent of MCS 395 | constant_offset=(not training)) # fix offset for evaluation 396 | else: 397 | self.frequency_offset = None 398 | 399 | 400 | ################ 401 | ##### MISC ##### 402 | ################ 403 | 404 | # Load covariance matrices 405 | if self.system in ("baseline_lmmse_kbest", "baseline_lmmse_lmmse"): 406 | 407 | # test if files exist 408 | fn = f'../weights/{self.label}_time_cov_mat.npy' 409 | if not exists(fn): 410 | raise FileNotFoundError("time_cov_mat.npy not found. " \ 411 | "Please run compute_cov_mat.py for given config first.") 412 | 413 | self.space_cov_mat = tf.cast(np.load( 414 | f'../weights/{self.label}_space_cov_mat.npy'), 415 | tf.complex64) 416 | self.time_cov_mat = tf.cast(np.load( 417 | f'../weights/{self.label}_time_cov_mat.npy'), 418 | tf.complex64) 419 | self.freq_cov_mat = tf.cast(np.load( 420 | f'../weights/{self.label}_freq_cov_mat.npy'), 421 | tf.complex64) 422 | -------------------------------------------------------------------------------- /weights/e2e_baseline_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/e2e_baseline_weights -------------------------------------------------------------------------------- /weights/e2e_large_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/e2e_large_weights -------------------------------------------------------------------------------- /weights/e2e_rt_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/e2e_rt_weights -------------------------------------------------------------------------------- /weights/nrx_large_64qam_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_large_64qam_weights -------------------------------------------------------------------------------- /weights/nrx_large_qpsk_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_large_qpsk_weights -------------------------------------------------------------------------------- /weights/nrx_large_var_mcs_64qam_masking_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_large_var_mcs_64qam_masking_weights -------------------------------------------------------------------------------- /weights/nrx_large_var_mcs_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_large_var_mcs_weights -------------------------------------------------------------------------------- /weights/nrx_large_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_large_weights -------------------------------------------------------------------------------- /weights/nrx_rt_64qam_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_rt_64qam_weights -------------------------------------------------------------------------------- /weights/nrx_rt_var_mcs_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_rt_var_mcs_weights -------------------------------------------------------------------------------- /weights/nrx_rt_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_rt_weights -------------------------------------------------------------------------------- /weights/nrx_site_specific_100k_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_site_specific_100k_weights -------------------------------------------------------------------------------- /weights/nrx_site_specific_baseline_large_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_site_specific_baseline_large_weights -------------------------------------------------------------------------------- /weights/nrx_site_specific_baseline_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_site_specific_baseline_weights -------------------------------------------------------------------------------- /weights/nrx_site_specific_large_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_site_specific_large_weights -------------------------------------------------------------------------------- /weights/nrx_site_specific_weights: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVlabs/neural_rx/d4c5420a9ee0e62c8e646551152714b02439ba6a/weights/nrx_site_specific_weights --------------------------------------------------------------------------------