anomaly-detection-material-parameters-calibration

Sionna param calibration (research proj)
git clone https://git.ea.contact/anomaly-detection-material-parameters-calibration
Log | Files | Refs | README

pusch_dmrs_config.py (12376B)


      1 #
      2 # SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
      3 # SPDX-License-Identifier: Apache-2.0
      4 #
      5 """PUSCH DMRS configuration for the nr (5G) sub-package of the Sionna library.
      6 """
      7 # pylint: disable=line-too-long
      8 
      9 from collections.abc import Sequence
     10 import numpy as np
     11 from .config import Config
     12 
     13 class PUSCHDMRSConfig(Config):
     14     """
     15     The PUSCHDMRSConfig objects sets parameters related to the generation
     16     of demodulation reference signals (DMRS) for a physical uplink shared
     17     channel (PUSCH), as described in Section 6.4.1.1 [3GPP38211]_.
     18 
     19     All configurable properties can be provided as keyword arguments during the
     20     initialization or changed later.
     21 
     22     Example
     23     -------
     24     >>> dmrs_config = PUSCHDMRSConfig(config_type=2)
     25     >>> dmrs_config.additional_position = 1
     26     """
     27 
     28     def __init__(self, **kwargs):
     29         self._name = "PUSCH DMRS Configuration"
     30         super().__init__(**kwargs)
     31         self.check_config()
     32 
     33     #-----------------------------#
     34     #---Configurable parameters---#
     35     #-----------------------------#
     36 
     37     #---config_type---#
     38     @property
     39     def config_type(self):
     40         """
     41         int, 1 (default) | 2 : DMRS configuration type
     42 
     43             The configuration type determines the frequency density of
     44             DMRS signals. With configuration type 1, six subcarriers per PRB are
     45             used for each antenna port, with configuration type 2, four
     46             subcarriers are used.
     47         """
     48         self._ifndef("config_type", 1)
     49         return self._config_type
     50 
     51     @config_type.setter
     52     def config_type(self, value):
     53         assert value in [1,2], "config_type must be in [1,2]"
     54         self._config_type = value
     55 
     56     #---type_a_position---#
     57     @property
     58     def type_a_position(self):
     59         """
     60         int, 2 (default) | 3 :  Position of first DMRS OFDM symbol
     61 
     62             Defines the position of the first DMRS symbol within a slot.
     63             This parameter only applies if the property
     64             :class:`~sionna.nr.PUSCHConfig.mapping_type` of
     65             :class:`~sionna.nr.PUSCHConfig` is equal to "A".
     66         """
     67         self._ifndef("type_a_position", 2)
     68         return self._type_a_position
     69 
     70     @type_a_position.setter
     71     def type_a_position(self, value):
     72         assert value in [2,3], "type_a_position must be in [2,3]"
     73         self._type_a_position = value
     74 
     75     #---additional_position---#
     76     @property
     77     def additional_position(self):
     78         """
     79         int, 0 (default) | 1 | 2 | 3 : Maximum number of additional DMRS positions
     80 
     81             The actual number of used DMRS positions depends on
     82             the length of the PUSCH symbol allocation.
     83         """
     84         self._ifndef("additional_position", 0)
     85         return self._additional_position
     86 
     87     @additional_position.setter
     88     def additional_position(self, value):
     89         assert value in [0,1,2,3], "additional_position must be in [0,1,2,3]"
     90         self._additional_position = value
     91 
     92     #---length---#
     93     @property
     94     def length(self):
     95         """
     96         int, 1 (default) | 2 : Number of front-loaded DMRS symbols
     97             A value of 1 corresponds to "single-symbol" DMRS, a value
     98             of 2 corresponds to "double-symbol" DMRS.
     99         """
    100         self._ifndef("length", 1)
    101         return self._length
    102 
    103     @length.setter
    104     def length(self, value):
    105         assert value in [1,2], "Invalid DMRS length"
    106         self._length = value
    107 
    108     #---dmrs_port_set---#
    109     @property
    110     def dmrs_port_set(self):
    111         """
    112         list, [] (default) | [0,...,11] : List of used DMRS antenna ports
    113 
    114             The elements in this list must all be from the list of
    115             `allowed_dmrs_ports` which depends on the `config_type` as well as
    116             the `length`. If set to `[]`, the port set will be equal to
    117             [0,...,num_layers-1], where
    118             :class:`~sionna.nr.PUSCHConfig.num_layers` is a property of the
    119             parent :class:`~sionna.nr.PUSCHConfig` instance.
    120         """
    121         self._ifndef("dmrs_port_set", [])
    122         return self._dmrs_port_set
    123 
    124     @dmrs_port_set.setter
    125     def dmrs_port_set(self, value):
    126         if isinstance(value, int):
    127             value = [value]
    128         elif isinstance(value, Sequence):
    129             value = list(value)
    130         else:
    131             raise ValueError("dmrs_port_set must be an integer or list")
    132         self._dmrs_port_set = value
    133 
    134     #---n_id---#
    135     @property
    136     def n_id(self):
    137         r"""
    138         2-tuple, None (default), [[0,...,65535], [0,...,65535]]: Scrambling
    139             identities
    140 
    141             Defines the scrambling identities :math:`N_\text{ID}^0` and
    142             :math:`N_\text{ID}^1` as a 2-tuple of integers. If `None`,
    143             the property :class:`~sionna.nr.CarrierConfig.n_cell_id` of the
    144             :class:`~sionna.nr.CarrierConfig` is used.
    145         """
    146         self._ifndef("n_id", None)
    147         return self._n_id
    148 
    149     @n_id.setter
    150     def n_id(self, value):
    151         if value is None:
    152             self._n_id = None
    153         elif isinstance(value, int):
    154             assert value in list(range(65536)), "n_id must be in [0, 65535]"
    155             self._n_id = [value, value]
    156         else:
    157             assert len(value)==2, "n_id must be either [] or a two-tuple"
    158             for e in value:
    159                 assert e in list(range(65536)), "Each element of n_id must be in [0, 65535]"
    160             self._n_id = value
    161 
    162     #---n_scid---#
    163     @property
    164     def n_scid(self):
    165         r"""
    166         int, 0 (default) | 1 : DMRS scrambling initialization
    167             :math:`n_\text{SCID}`
    168         """
    169         self._ifndef("n_scid", 0)
    170         return self._n_scid
    171 
    172     @n_scid.setter
    173     def n_scid(self, value):
    174         assert value in [0, 1], "n_scid must be 0 or 1"
    175         self._n_scid = value
    176 
    177     #---num_cdm_groups_without_data---#
    178     @property
    179     def num_cdm_groups_without_data(self):
    180         """
    181         int, 2 (default) | 1 | 3 : Number of CDM groups without data
    182 
    183             This parameter controls how many REs are available for data
    184             transmission in a DMRS symbol. It should be greater or equal to
    185             the maximum configured number of CDM groups. A value of
    186             1 corresponds to CDM group 0, a value of 2 corresponds to
    187             CDM groups 0 and 1, and a value of 3 corresponds to
    188             CDM groups 0, 1, and 2.
    189         """
    190         self._ifndef("num_cdm_groups_without_data", 2)
    191         return self._num_cdm_groups_without_data
    192 
    193     @num_cdm_groups_without_data.setter
    194     def num_cdm_groups_without_data(self, value):
    195         assert value in [1,2,3], \
    196             "num_cdm_groups_without_data must be in [1,2,3]"
    197         self._num_cdm_groups_without_data = value
    198 
    199     #-----------------------------#
    200     #---Read-only parameters------#
    201     #-----------------------------#
    202 
    203     @property
    204     def allowed_dmrs_ports(self):
    205         """
    206         list, [0,...,max_num_dmrs_ports-1], read-only : List of nominal antenna
    207             ports
    208 
    209             The maximum number of allowed antenna ports `max_num_dmrs_ports`
    210             depends on the DMRS `config_type` and `length`. It can be
    211             equal to 4, 6, 8, or 12.
    212         """
    213         if self.length==1:
    214             if self.config_type==1:
    215                 if self.num_cdm_groups_without_data==1:
    216                     return [0,1]
    217                 else:
    218                     return [0,1,2,3]
    219                 #max_num_dmrs_ports = self.num_cdm_groups_without_data*2
    220             elif self.config_type==2:
    221                 if self.num_cdm_groups_without_data==1:
    222                     return [0,1]
    223                 elif self.num_cdm_groups_without_data==2:
    224                     return [0,1,2,3]
    225                 else:
    226                     return [0,1,2,3,4,5]
    227                 #max_num_dmrs_ports = self.num_cdm_groups_without_data*2
    228         elif self.length==2:
    229             if self.config_type==1:
    230                 if self.num_cdm_groups_without_data==1:
    231                     return [0,1,4,5]
    232                 else:
    233                     return [0,1,2,3,4,5,6,7]
    234                 #max_num_dmrs_ports = self.num_cdm_groups_without_data*4
    235             elif self.config_type==2:
    236                 if self.num_cdm_groups_without_data==1:
    237                     return [0,1,6,7]
    238                 elif self.num_cdm_groups_without_data==2:
    239                     return [0,1,2,3,6,7,8,9]
    240                 else:
    241                     return [0,1,2,3,4,5,6,7,8,9,10,11]
    242                 #max_num_dmrs_ports = self.num_cdm_groups_without_data*4
    243         #return list(range(max_num_dmrs_ports))
    244 
    245     @property
    246     def cdm_groups(self):
    247         r"""
    248         list, elements in [0,1,2], read-only : List of CDM groups
    249             :math:`\lambda` for all ports
    250             in the `dmrs_port_set` as defined in
    251             Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]_
    252 
    253             Depends on the `config_type`.
    254         """
    255         if self.config_type==1:
    256             cdm_groups = [0,0,1,1,0,0,1,1]
    257         else:
    258             cdm_groups = [0,0,1,1,2,2,0,0,1,1,2,2]
    259         return [cdm_groups[port] for port in self.dmrs_port_set]
    260 
    261     @property
    262     def deltas(self):
    263         r"""
    264         list, elements in [0,1,2,4], read-only : List of delta (frequency)
    265             shifts :math:`\Delta` for all ports in the `port_set` as defined in
    266             Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]_
    267 
    268             Depends on the `config_type`.
    269         """
    270         if self.config_type==1:
    271             deltas = [0,0,1,1,0,0,1,1]
    272         else:
    273             deltas = [0,0,2,2,4,4,0,0,2,2,4,4]
    274         return [deltas[port] for port in self.dmrs_port_set]
    275 
    276     @property
    277     def w_f(self):
    278         r"""
    279         matrix, elements in [-1,1], read-only : Frequency weight vectors
    280             :math:`w_f(k')` for all ports in the port set as defined in
    281             Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]_
    282         """
    283         if self.config_type==1:
    284             w_f = np.array([[1, 1,1, 1,1, 1,1, 1],
    285                             [1,-1,1,-1,1,-1,1,-1]])
    286         else: # config_type == 2
    287             w_f = np.array([[1, 1,1, 1,1, 1,1, 1,1, 1,1, 1],
    288                             [1,-1,1,-1,1,-1,1,-1,1,-1,1,-1]])
    289         return w_f[:, self.dmrs_port_set]
    290 
    291     @property
    292     def w_t(self):
    293         r"""
    294         matrix, elements in [-1,1], read-only : Time weight vectors
    295             :math:`w_t(l')` for all ports in the port set as defined in
    296             Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]_
    297         """
    298         if self.config_type==1:
    299             w_t = np.array([[1,1,1,1, 1, 1, 1, 1],
    300                            [1,1,1,1,-1,-1,-1,-1]])
    301         else: # config_type == 2
    302             w_t = np.array([[1,1,1,1,1,1, 1, 1, 1, 1, 1, 1],
    303                             [1,1,1,1,1,1,-1,-1,-1,-1,-1,-1]])
    304         return w_t[:, self.dmrs_port_set]
    305 
    306     @property
    307     def beta(self):
    308         r"""
    309         float, read-only : Ratio of PUSCH energy per resource element
    310             (EPRE) to DMRS EPRE :math:`\beta^{\text{DMRS}}_\text{PUSCH}`
    311             Table 6.2.2-1 [3GPP38214]_
    312         """
    313         if self.num_cdm_groups_without_data==1:
    314             return 1.0
    315         elif self.num_cdm_groups_without_data==2:
    316             return np.sqrt(2)
    317         elif self.num_cdm_groups_without_data==3:
    318             if self.config_type==2:
    319                 return np.sqrt(3)
    320 
    321     #-------------------#
    322     #---Class methods---#
    323     #-------------------#
    324 
    325     def check_config(self):
    326         """Test if configuration is valid"""
    327 
    328         if self.length==2:
    329             assert self.additional_position in [0, 1], \
    330                 "additional_position must be in [0, 1] for length==2"
    331 
    332         for p in self.dmrs_port_set:
    333             assert p in self.allowed_dmrs_ports,\
    334                 f"Unallowed DMRS port {p}. Not in {self.allowed_dmrs_ports}."
    335 
    336         if self.config_type==1:
    337             assert self.num_cdm_groups_without_data in [1, 2], \
    338             "num_cdm_groups_without_data must be in [1,2] for config_type 1"
    339 
    340         attr_list = ["config_type",
    341                      "type_a_position",
    342                      "additional_position",
    343                      "length",
    344                      "dmrs_port_set",
    345                      "n_id",
    346                      "n_scid",
    347                      "num_cdm_groups_without_data"
    348                     ]
    349         for attr in attr_list:
    350             value = getattr(self, attr)
    351             setattr(self, attr, value)