├── .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
--------------------------------------------------------------------------------