5G NR

This module provides blocks and functions to support simulations of 5G NR compliant features, in particular, the physical uplink shared channel (PUSCH). It provides implementations of a subset of the physical layer functionalities as described in the 3GPP specifications [3GPP38211], [3GPP38212], and [3GPP38214].

The best way to discover this module’s components is by having a look at the 5G NR PUSCH Tutorial.

The following code snippet shows how you can make standard-compliant simulations of the 5G NR PUSCH with a few lines of code:

# Create a PUSCH configuration with default settings
pusch_config = PUSCHConfig()

# Instantiate a PUSCHTransmitter from the PUSCHConfig
pusch_transmitter = PUSCHTransmitter(pusch_config)

# Create a PUSCHReceiver using the PUSCHTransmitter
pusch_receiver = PUSCHReceiver(pusch_transmitter)

# AWGN channel
channel = AWGN()

# Simulate transmissions over the AWGN channel
batch_size = 16
no = 0.1 # Noise variance

x, b = pusch_transmitter(batch_size) # Generate transmit signal and info bits

y = channel(x, no) # Simulate channel output

b_hat = pusch_receiver(y, no) # Recover the info bits

# Compute BER
print("BER:", compute_ber(b, b_hat).numpy())

The PUSCHTransmitter and PUSCHReceiver provide high-level abstractions of all required processing blocks. You can easily modify them according to your needs.

Carrier

class sionna.phy.nr.CarrierConfig(**kwargs)[source]

The CarrierConfig objects sets parameters for a specific OFDM numerology, as described in Section 4 [3GPP38211].

All configurable properties can be provided as keyword arguments during the initialization or changed later.

Example

>>> carrier_config = CarrierConfig(n_cell_id=41)
>>> carrier_config.subcarrier_spacing = 30
property cyclic_prefix

Get/set cyclic prefix length

The option “normal” corresponds to 14 OFDM symbols per slot, while “extended” corresponds to 12 OFDM symbols. The latter option is only possible with a subcarrier_spacing of 60 kHz.

Type:

“normal” (default) | “extended”

property cyclic_prefix_length

Cyclic prefix length NCP,lμTc [s]

Type:

float

property frame_duration

Duration of a frame Tf [s]

Type:

float, (default 10e-3)

property frame_number

Get/set system frame number nf

Type:

int, (default 0), [0,…,1023]

property kappa

The constant κ=Ts/Tc

Type:

float, (default 64)

property mu

Subcarrier spacing configuration, Δf=2μ15 kHz

Type:

int, (default 0) | 1 | 2 | 3 | 4 | 5 | 6

property n_cell_id

Physical layer cell identity NIDcell

Type:

int, (default 1) | [0,…,1007]

property n_size_grid

Get/set number of resource blocks in the carrier resource grid Ngrid,xsize,μ

Type:

int, (default 4) | [1,…,275]

property n_start_grid

Get/set start of resource grid relative to common resource block (CRB) 0 Ngrid,xstart,μ

Type:

int, (default 0) | [0,…,2199]

property num_slots_per_frame

Number of slots per frame Nslotframe,μ

Depends on the subcarrier_spacing.

Type:

int, (default 10) | 20 | 40 | 80 | 160 | 320 | 640

property num_slots_per_subframe

Number of slots per subframe Nslotsubframe,μ

Depends on the subcarrier_spacing.

Type:

int, (default 1) | 2 | 4 | 8 | 16 | 32 | 64

property num_symbols_per_slot

Number of OFDM symbols per slot Nsymbslot

Configured through the cyclic_prefix.

Type:

int, (default 14) | 12

property slot_number

Get/set slot number within a frame ns,fμ

Type:

int, (default 0), [0,…,num_slots_per_frame]

property sub_frame_duration

Duration of a subframe Tsf [s]

Type:

float, (default 1e-3), read-only

property subcarrier_spacing

Get/set subcarrier spacing Δf [kHz]

Type:

float, (default 15) | 30 | 60 | 120 | 240 | 480 | 960

property t_c

Sampling time Tc [s] for subcarrier spacing 480kHz

Type:

float, (default 0.509e-9)

property t_s

Sampling time Ts [s] for subcarrier spacing 15kHz

Type:

float, (default 32.552e-9)

Layer Mapping

class sionna.phy.nr.LayerMapper(num_layers=1, verbose=False, precision=None, **kwargs)[source]

Performs MIMO layer mapping of modulated symbols to layers as defined in [3GPP38211].

The LayerMapper supports PUSCH and PDSCH channels and follows the procedure as defined in Sec. 6.3.1.3 and Sec. 7.3.1.3 in [3GPP38211], respectively.

As specified in Tab. 7.3.1.3.-1 [3GPP38211], the LayerMapper expects two input streams for multiplexing if more than 4 layers are active (only relevant for PDSCH).

Parameters:
  • num_layers (int, (default 1) | [1,…,8]) – Number of MIMO layers. If num_layers >=4, a list of two inputs is expected.

  • verbose (bool, (default False)) – If True, additional parameters are printed.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:

inputs ([…,n], or [[…,n1], […,n2]], tf.complex) – Sequence of symbols to be mapped. If num_layers >=4, a list of two inputs is expected and n1/n2 must be chosen as defined in Tab. 7.3.1.3.-1 [3GPP38211].

Output:

[…,num_layers, n/num_layers], tf.complex – Sequence of symbols mapped to the MIMO layers

property num_codewords

Number of input codewords for layer mapping. Can be either 1 or 2.

Type:

int

property num_layers

Number of MIMO layers

Type:

int

property num_layers0

Number of layers for first codeword (only relevant for num_codewords =2)

Type:

int

property num_layers1

Number of layers for second codeword (only relevant for num_codewords =2)

Type:

int

class sionna.phy.nr.LayerDemapper(layer_mapper, num_bits_per_symbol=1, precision=None, **kwargs)[source]

Demaps MIMO layers to coded transport block(s) by following Sec. 6.3.1.3 and Sec. 7.3.1.3 in [3GPP38211].

This block must be associated to a LayerMapper and performs the inverse operation.

It is assumed that num_bits_per_symbol consecutive LLRs belong to a single symbol position. This allows to apply the LayerDemapper after demapping symbols to LLR values.

If the layer mapper is configured for dual codeword transmission, a list of both transport block streams is returned.

Parameters:
  • layer_mapper (LayerMapper) – Associated LayerMapper

  • num_bits_per_symbol (int, (default 1)) – Modulation order. Defines how many consecutive LLRs are associated to the same symbol position.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:

inputs ([…,num_layers, n/num_layers], tf.float) – MIMO layer data sequences

Output:

[…,n], or [[…,n1], […,n2]], tf.float – Sequence of bits after layer demapping. If num_codewords =2, a list of two transport blocks is returned.

Note

As it is more convenient to apply the layer demapper after demapping symbols to LLRs, this block groups the input sequence into groups of num_bits_per_symbol LLRs before restoring the original symbol sequence. This behavior can be deactivated by setting num_bits_per_symbol =1.

PUSCH

class sionna.phy.nr.PUSCHConfig(carrier_config=None, pusch_dmrs_config=None, tb_config=None, **kwargs)[source]

The PUSCHConfig objects sets parameters for a physical uplink shared channel (PUSCH), as described in Sections 6.3 and 6.4 [3GPP38211].

All configurable properties can be provided as keyword arguments during the initialization or changed later.

Parameters:

Example

>>> pusch_config = PUSCHConfig(mapping_type="B")
>>> pusch_config.dmrs.config_type = 2
>>> pusch_config.carrier.subcarrier_spacing = 30
c_init(l)[source]

Compute RNG initialization cinit as in Section 6.4.1.1.1.1 [3GPP38211]

Input:

l (int) – OFDM symbol index relative to a reference l

Output:

c_init (int) – RNG initialization value

property carrier

Carrier configuration

Type:

CarrierConfig

property dmrs

PUSCH DMRS configuration

Type:

PUSCHDMRSConfig

property dmrs_grid

Empty resource grid for each DMRS port, filled with DMRS signals

This property returns for each configured DMRS port an empty resource grid filled with DMRS signals as defined in Section 6.4.1.1 [3GPP38211]. Not all possible options are implemented, e.g., frequency hopping and transform precoding are not available.

This property provides the unprecoded DMRS for each configured DMRS port. Precoding might be applied to map the DMRS to the antenna ports. However, in this case, the number of DMRS ports cannot be larger than the number of layers.

Type:

complex, [num_dmrs_ports, num_subcarriers, num_symbols_per_slot], read-only

property dmrs_mask

Masked resource elements in the resource grid. True corresponds to resource elements on which no data is transmitted.

Type:

bool, [num_subcarriers, num_symbols_per_slot], read-only

property dmrs_symbol_indices

Indices of DMRS symbols within a slot

Type:

list, int, read-only

property frequency_hopping

Frequency hopping configuration

Type:

str, “neither” (default), read-only

property l_bar

List of possible values of l¯ used for DMRS generation

Defined in Tables 6.4.1.1.3-3 and 6.4.1.1.3-4 [3GPP38211].

Type:

list, elements in [0,…,11], read-only

property mapping_type

Mapping type

Type:

str, “A” (default) | “B”

property n_rnti

Radio network temporary identifier nRNTI

Type:

int, 1 (default), [0,…,65535]

property n_size_bwp

Number of resource blocks in the bandwidth part (BWP) NBWP,isize,μ

If set to None, the property n_size_grid of carrier will be used.

Type:

int, None (default) | [1,…,275]

property n_start_bwp

Start of BWP relative to common resource block (CRB) 0 NBWP,istart,μ

Type:

int, 0 (default) | [0,…,2199]

property num_antenna_ports

Number of antenna ports

Must be larger than or equal to num_layers.

Type:

int, 1 (default) | 2 | 4

property num_coded_bits

Number of coded bits that fit into one PUSCH slot.

Type:

int, read-only

property num_layers

Number of transmission layers ν

Must be smaller than or equal to num_antenna_ports.

Type:

int, 1 (default) | 2 | 3 | 4

property num_ov

Number of unused resource elements due to additional overhead as specified by higher layer.

Type:

int, 0 (default), read-only

property num_res_per_prb

Number of resource elements per PRB available for data

Type:

int, read-only

property num_resource_blocks

Number of allocated resource blocks for the PUSCH transmissions.

Type:

int, read-only

property num_subcarriers

Number of allocated subcarriers for the PUSCH transmissions

Type:

int, read-only

property precoding

PUSCH transmission scheme

Type:

str, “non-codebook” (default), “codebook”

property precoding_matrix

Precoding matrix W as defined in Tables 6.3.1.5-1 to 6.3.1.5-7 [3GPP38211].

Only relevant if precoding is “codebook”.

Type:

nd_array, complex, [num_antenna_ports, numLayers]

show()[source]

Print all properties of the PUSCHConfig and children

property symbol_allocation

PUSCH symbol allocation

The first elements denotes the start of the symbol allocation. The second denotes the positive number of allocated OFDM symbols. For mapping_type “A”, the first element must be zero. For mapping_type “B”, the first element must be in [0,…,13]. The second element must be such that the index of the last allocated OFDM symbol is not larger than 13 (for “normal” cyclic prefix) or 11 (for “extended” cyclic prefix).

Type:

2-tuple, int, [0, 14] (default)

property tb

Transport block configuration

Type:

TBConfig

property tb_size

Transport block size, i.e., how many information bits can be encoded into a slot for the given slot configuration

Type:

int, read-only

property tpmi

Transmit precoding matrix indicator

The allowed value depends on the number of layers and the number of antenna ports according to Table 6.3.1.5-1 until Table 6.3.1.5-7 [3GPP38211].

Type:

int, 0 (default) | [0,…,27]

property transform_precoding

Use transform precoding

Type:

bool, False (default)

class sionna.phy.nr.PUSCHDMRSConfig(**kwargs)[source]

The PUSCHDMRSConfig objects sets parameters related to the generation of demodulation reference signals (DMRS) for a physical uplink shared channel (PUSCH), as described in Section 6.4.1.1 [3GPP38211].

All configurable properties can be provided as keyword arguments during the initialization or changed later.

Example

>>> dmrs_config = PUSCHDMRSConfig(config_type=2)
>>> dmrs_config.additional_position = 1
property additional_position

Maximum number of additional DMRS positions

The actual number of used DMRS positions depends on the length of the PUSCH symbol allocation.

Type:

int, 0 (default) | 1 | 2 | 3

property allowed_dmrs_ports

List of nominal antenna ports

The maximum number of allowed antenna ports max_num_dmrs_ports depends on the DMRS config_type and length. It can be equal to 4, 6, 8, or 12.

Type:

list, [0,…,max_num_dmrs_ports-1], read-only

property beta

Ratio of PUSCH energy per resource element (EPRE) to DMRS EPRE βPUSCHDMRS Table 6.2.2-1 [3GPP38214]

Type:

float, read-only

property cdm_groups

List of CDM groups λ for all ports in the dmrs_port_set as defined in Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]

Depends on the config_type.

Type:

list, elements in [0,1,2], read-only

property config_type

DMRS configuration type

The configuration type determines the frequency density of DMRS signals. With configuration type 1, six subcarriers per PRB are used for each antenna port, with configuration type 2, four subcarriers are used.

Type:

int, 1 (default) | 2

property deltas

List of delta (frequency) shifts Δ for all ports in the port_set as defined in Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]

Depends on the config_type.

Type:

list, elements in [0,1,2,4], read-only

property dmrs_port_set

List of used DMRS antenna ports

The elements in this list must all be from the list of allowed_dmrs_ports which depends on the config_type as well as the length. If set to [], the port set will be equal to [0,…,num_layers-1], where num_layers is a property of the parent PUSCHConfig instance.

Type:

list, [] (default) | [0,…,11]

property length

Number of front-loaded DMRS symbols A value of 1 corresponds to “single-symbol” DMRS, a value of 2 corresponds to “double-symbol” DMRS.

Type:

int, 1 (default) | 2

property n_id

Scrambling identities

Defines the scrambling identities NID0 and NID1 as a 2-tuple of integers. If None, the property n_cell_id of the CarrierConfig is used.

Type:

2-tuple, None (default), [[0,…,65535], [0,…,65535]]

property n_scid

DMRS scrambling initialization nSCID

Type:

int, 0 (default) | 1

property num_cdm_groups_without_data

Number of CDM groups without data

This parameter controls how many REs are available for data transmission in a DMRS symbol. It should be greater or equal to the maximum configured number of CDM groups. A value of 1 corresponds to CDM group 0, a value of 2 corresponds to CDM groups 0 and 1, and a value of 3 corresponds to CDM groups 0, 1, and 2.

Type:

int, 2 (default) | 1 | 3

property type_a_position

Position of first DMRS OFDM symbol

Defines the position of the first DMRS symbol within a slot. This parameter only applies if the property mapping_type of PUSCHConfig is equal to “A”.

Type:

int, 2 (default) | 3

property w_f

Frequency weight vectors wf(k) for all ports in the port set as defined in Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]

Type:

matrix, elements in [-1,1], read-only

property w_t

Time weight vectors wt(l) for all ports in the port set as defined in Table 6.4.1.1.3-1 or 6.4.1.1.3-2 [3GPP38211]

Type:

matrix, elements in [-1,1], read-only

class sionna.phy.nr.PUSCHLSChannelEstimator(resource_grid, dmrs_length, dmrs_additional_position, num_cdm_groups_without_data, interpolation_type='nn', interpolator=None, precision=None, **kwargs)[source]

Block implementing least-squares (LS) channel estimation for NR PUSCH Transmissions

After LS channel estimation at the pilot positions, the channel estimates and error variances are interpolated accross the entire resource grid using a specified interpolation function.

The implementation is similar to that of LSChannelEstimator. However, it additional takes into account the separation of streams in the same CDM group as defined in PUSCHDMRSConfig. This is done through frequency and time averaging of adjacent LS channel estimates.

Parameters:
  • resource_grid (ResourceGrid) – ResourceGrid to be used

  • dmrs_length (int, 1 | 2) – Length of DMRS symbols. See PUSCHDMRSConfig.

  • dmrs_additional_position (int, 0 | 1 | 2 | 3) – Number of additional DMRS symbols. See PUSCHDMRSConfig.

  • num_cdm_groups_without_data (int, 1 | 2 | 3) – Number of CDM groups masked for data transmissions. See PUSCHDMRSConfig.

  • interpolation_type ("nn" (default) | "lin" | "lin_time_avg") – The interpolation method to be used. It is ignored if interpolator is not None. Available options are NearestNeighborInterpolator (“nn”) or LinearInterpolator without (“lin”) or with averaging across OFDM symbols (“lin_time_avg”).

  • interpolator (None (default) | BaseChannelInterpolator) – Interpolator such as LMMSEInterpolator, or None. In the latter case, the interpolator specified by interpolation_type is used. Otherwise, the interpolator is used and interpolation_type is ignored.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:
  • y ([batch_size, num_rx, num_rx_ant, num_ofdm_symbols,fft_size], tf.complex) – Observed resource grid

  • no ([batch_size, num_rx, num_rx_ant] or only the first n>=0 dims, tf.float) – Variance of the AWGN

Output:
  • h_ls ([batch_size, num_rx, num_rx_ant, num_tx, num_streams_per_tx, num_ofdm_symbols,fft_size], tf.complex) – Channel estimates across the entire resource grid for all transmitters and streams

  • err_var (Same shape as h_ls, tf.float) – Channel estimation error variance across the entire resource grid for all transmitters and streams

class sionna.phy.nr.PUSCHPilotPattern(pusch_configs, precision=None)[source]

Class defining a pilot pattern for NR PUSCH

This class defines a PilotPattern that is used to configure an OFDM ResourceGrid.

For every transmitter, a separte PUSCHConfig needs to be provided from which the pilot pattern will be created.

Parameters:
  • pusch_configs (instance or list of PUSCHConfig) – PUSCH Configurations according to which the pilot pattern will created. One configuration is needed for each transmitter.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

property cdtype

Type for complex floating point numbers

Type:

tf.complex

property mask

Mask of the pilot pattern

Type:

[num_tx, num_streams_per_tx, num_ofdm_symbols, num_effective_subcarriers], bool

property normalize

Get/set if the pilots are normalized or not

Type:

bool

property num_data_symbols

Number of data symbols per transmit stream

Type:

int

property num_effective_subcarriers

Number of effectvie subcarriers

Type:

int

property num_ofdm_symbols

Number of OFDM symbols

Type:

int

property num_pilot_symbols

Number of pilot symbols per transmit stream

Type:

int

property num_streams_per_tx

Number of streams per transmitter

Type:

int

property num_tx

Number of transmitters

Type:

int

property pilots

Get/set the possibly normalized tensor of pilot symbols. If pilots are normalized, the normalization will be applied after new values for pilots have been set. If this is not the desired behavior, turn normalization off.

Type:

[num_tx, num_streams_per_tx, num_pilots], tf.complex

property precision

Precision used for all compuations

Type:

str, “single” | “double”

property rdtype

Type for real floating point numbers

Type:

tf.float

show(tx_ind=None, stream_ind=None, show_pilot_ind=False)

Visualizes the pilot patterns for some transmitters and streams.

Input:
  • tx_ind (None (default) | int`| `list, int) – Indicates the indices of transmitters to be included. If None, all transmitters included.

  • stream_ind (None (default) | int`| `list, int) – Indicates the indices of streams to be included. If None, all streams included.

  • show_pilot_ind (bool, (default False)) – Indicates if the indices of the pilot symbols should be shown

Output:

list (matplotlib.figure.Figure) – List of matplot figure objects showing each the pilot pattern from a specific transmitter and stream

class sionna.phy.nr.PUSCHPrecoder(precoding_matrices, precision=None, **kwargs)[source]

Precodes a batch of modulated symbols mapped onto a resource grid for PUSCH transmissions.

Each transmitter is assumed to have its own precoding matrix.

Parameters:
  • precoding_matrices (list, [num_tx, num_antenna_ports, num_layers]. tf.complex) – List of precoding matrices, one for each transmitter. All precoding matrices must have the same shape.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:

[batch_size, num_tx, num_layers, num_symbols_per_slot,num_subcarriers], tf.complex – Batch of resource grids to be precoded

Output:

[batch_size, num_tx, num_antenna_ports, num_symbols_per_slot, num_subcarriers], tf.complex – Batch of precoded resource grids

class sionna.phy.nr.PUSCHReceiver(pusch_transmitter, channel_estimator=None, mimo_detector=None, tb_decoder=None, return_tb_crc_status=False, stream_management=None, input_domain='freq', l_min=None, precision=None, **kwargs)[source]

This block implements a full receiver for batches of 5G NR PUSCH slots sent by multiple transmitters. Inputs can be in the time or frequency domain. Perfect channel state information can be optionally provided. Different channel estimatiors, MIMO detectors, and transport decoders can be configured.

The block combines multiple processing blocks into a single block as shown in the following figure. Blocks with dashed lines are optional and depend on the configuration.

../../_images/pusch_receiver_block_diagram.png

If the input_domain equals “time”, the inputs y are first transformed to resource grids with the OFDMDemodulator. Then channel estimation is performed, e.g., with the help of the PUSCHLSChannelEstimator. If channel_estimator is chosen to be “perfect”, this step is skipped and the input h is used instead. Next, MIMO detection is carried out with an arbitrary OFDMDetector. The resulting LLRs for each layer are then combined to transport blocks with the help of the LayerDemapper. Finally, the transport blocks are decoded with the TBDecoder.

Parameters:
  • pusch_transmitter (PUSCHTransmitter) – Transmitter used for the generation of the transmit signals

  • channel_estimator (None (default) | BaseChannelEstimator | “perfect”) – Channel estimator to be used. If None, the PUSCHLSChannelEstimator with linear interpolation is used. If “perfect”, no channel estimation is performed and the channel state information h must be provided as additional input.

  • mimo_detector (None (default) | OFDMDetector) – MIMO Detector to be used. If None, the LinearDetector with LMMSE detection is used.

  • tb_decoder (None (default) | TBDecoder) – Transport block decoder to be used. If None, the TBDecoder with its default settings is used.

  • return_tb_crc_status (bool, (default False)) – If True, the status of the transport block CRC is returned as additional output.

  • stream_management (None (default) | StreamManagement) – Stream management configuration to be used. If None, it is assumed that there is a single receiver which decodes all streams of all transmitters.

  • input_domain ("freq" (default) | "time") – Domain of the input signal. Defaults to “freq”

  • l_min (None (default) | int) – Smallest time-lag for the discrete complex baseband channel. Only needed if input_domain equals “time”.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:
  • y ([batch size, num_rx, num_rx_ant, num_ofdm_symbols, fft_size], tf.complex, or [batch size, num_rx, num_rx_ant, num_time_samples + l_max - l_min], tf.complex) – Frequency- or time-domain input signal

  • no ([batch_size, num_rx, num_rx_ant] or only the first n>=0 dims, tf.float) – Variance of the AWGN

  • h ([batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_ofdm_symbols, num_subcarriers], tf.complex, or [batch size, num_rx, num_rx_ant, num_tx, num_tx_ant, num_time_samples + l_max - l_min, l_max - l_min + 1], tf.complex) – Perfect channel state information in either frequency or time domain (depending on input_domain) to be used for detection. Only required if channel_estimator equals “perfect”.

Output:
  • b_hat ([batch_size, num_tx, tb_size], tf.float) – Decoded information bits

  • tb_crc_status ([batch_size, num_tx], tf.bool) – Transport block CRC status

Example

>>> pusch_config = PUSCHConfig()
>>> pusch_transmitter = PUSCHTransmitter(pusch_config)
>>> pusch_receiver = PUSCHReceiver(pusch_transmitter)
>>> channel = AWGN()
>>> x, b = pusch_transmitter(16)
>>> no = 0.1
>>> y = channel([x, no])
>>> b_hat = pusch_receiver([x, no])
>>> compute_ber(b, b_hat)
<tf.Tensor: shape=(), dtype=float64, numpy=0.0>
property resource_grid

OFDM resource grid underlying the PUSCH transmissions

class sionna.phy.nr.PUSCHTransmitter(pusch_configs, return_bits=True, output_domain='freq', precision=None, verbose=False, **kwargs)[source]

This block generates batches of 5G NR PUSCH slots for multiple transmitters with random or provided payloads. Frequency- or time-domain outputs can be generated.

It combines multiple processing blocks into a single layer as shown in the following figure. Blocks with dashed lines are optional and depend on the configuration.

../../_images/pusch_transmitter_block_diagram.png

Information bits b that are either randomly generated or provided as input are encoded into a transport block by the TBEncoder. The encoded bits are then mapped to QAM constellation symbols by the Mapper. The LayerMapper splits the modulated symbols into different layers which are then mapped onto OFDM resource grids by the ResourceGridMapper. If precoding is enabled in the PUSCHConfig, the resource grids are further precoded so that there is one for each transmitter and antenna port. If output_domain equals “freq”, these are the outputs x. If output_domain is chosen to be “time”, the resource grids are transformed into time-domain signals by the OFDMModulator.

Parameters:
  • pusch_configs (instance or list of PUSCHConfig) – PUSCH Configurations according to which the resource grid and pilot pattern will created. One configuration is needed for each transmitter.

  • return_bits (bool, (default True)) – If set to True, the block generates random information bits to be transmitted and returns them together with the transmit signal.

  • output_domain ("freq" (default) | "time") – Domain of the output

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

  • verbose (bool, (default False)) – If True, additional parameters are printed during initialization.

Input:
  • One of

  • batch_size (int) – Batch size of random transmit signals to be generated, if return_bits is True.

  • b ([batch_size, num_tx, tb_size], tf.float) – Information bits to be transmitted, if return_bits is False.

Output:
  • x ([batch size, num_tx, num_tx_ant, num_ofdm_symbols, fft_size], tf.complex, or [batch size, num_tx, num_tx_ant, num_time_samples], tf.complex) – Transmit signal in either frequency or time domain, depending on output_domain.

  • b ([batch_size, num_tx, tb_size], tf.float) – Transmitted information bits. Only returned if return_bits is True.

Example

>>> pusch_config = PUSCHConfig()
>>> pusch_transmitter = PUSCHTransmitter(pusch_config)
>>> x, b = pusch_transmitter(16)
>>> print("Shape of x:", x.shape)
Shape of x: (16, 1, 1, 14, 48)
>>> print("Shape of b:", b.shape)
Shape of b: (16, 1, 1352)
property pilot_pattern

Aggregate pilot pattern of all transmitters

property resource_grid

OFDM resource grid underlying the PUSCH transmissions

show()[source]

Print all properties of the PUSCHConfig and children

Transport Block

class sionna.phy.nr.TBConfig(**kwargs)[source]

The TBConfig objects sets parameters related to the transport block encoding, as described in TS 38.214 [3GPP38214].

All configurable properties can be provided as keyword arguments during the initialization or changed later.

The TBConfig is configured by selecting the modulation and coding scheme (MCS) tables and index.

Example

>>> tb_config = TBConfig(mcs_index=13)
>>> tb_config.mcs_table = 3
>>> tb_config.channel_type = "PUSCH"
>>> tb_config.show()

The following tables provide an overview of the corresponding coderates and modulation orders.

Table 1 MCS Index Table 1 (Table 5.1.3.1-1 in [3GPP38214])
MCS Index
IMCS
Modulation Order
Qm
Target Coderate
R×[1024]
Spectral Efficiency

0

2

120

0.2344

1

2

157

0.3066

2

2

193

0.3770

3

2

251

0.4902

4

2

308

0.6016

5

2

379

0.7402

6

2

449

0.8770

7

2

526

1.0273

8

2

602

1.1758

9

2

679

1.3262

10

4

340

1.3281

11

4

378

1.4766

12

4

434

1.6953

13

4

490

1.9141

14

4

553

2.1602

15

4

616

2.4063

16

4

658

2.5703

17

6

438

2.5664

18

6

466

2.7305

19

6

517

3.0293

20

6

567

3.3223

21

6

616

3.6094

22

6

666

3.9023

23

6

719

4.2129

24

6

772

4.5234

25

6

822

4.8164

26

6

873

5.1152

27

6

910

5.3320

28

6

948

5.5547

Table 2 MCS Index Table 2 (Table 5.1.3.1-2 in [3GPP38214])
MCS Index
IMCS
Modulation Order
Qm
Target Coderate
R×[1024]
Spectral Efficiency

0

2

120

0.2344

1

2

193

0.3770

2

2

308

0.6016

3

2

449

0.8770

4

2

602

1.1758

5

4

378

1.4766

6

4

434

1.6953

7

4

490

1.9141

8

4

553

2.1602

9

4

616

2.4063

10

4

658

2.5703

11

6

466

2.7305

12

6

517

3.0293

13

6

567

3.3223

14

6

616

3.6094

15

6

666

3.9023

16

6

719

4.2129

17

6

772

4.5234

18

6

822

4.8164

19

6

873

5.1152

20

8

682.5

5.3320

21

8

711

5.5547

22

8

754

5.8906

23

8

797

6.2266

24

8

841

6.5703

25

8

885

6.9141

26

8

916.5

7.1602

27

8

948

7.4063

Table 3 MCS Index Table 3 (Table 5.1.3.1-3 in [3GPP38214])
MCS Index
IMCS
Modulation Order
Qm
Target Coderate
R×[1024]
Spectral Efficiency

0

2

30

0.0586

1

2

40

0.0781

2

2

50

0.0977

3

2

64

0.1250

4

2

78

0.1523

5

2

99

0.1934

6

2

120

0.2344

7

2

157

0.3066

8

2

193

0.3770

9

2

251

0.4902

10

2

308

0.6016

11

2

379

0.7402

12

2

449

0.8770

13

2

526

1.0273

14

2

602

1.1758

15

4

340

1.3281

16

4

378

1.4766

17

4

434

1.6953

18

4

490

1.9141

19

4

553

2.1602

20

4

616

2.4063

21

6

438

2.5564

22

6

466

2.7305

23

6

517

3.0293

24

6

567

3.3223

25

6

616

3.6094

26

6

666

3.9023

27

6

719

4.2129

28

6

772

4.5234

Table 4 MCS Index Table 4 (Table 5.1.3.1-4 in [3GPP38214])
MCS Index
IMCS
Modulation Order
Qm
Target Coderate
R×[1024]
Spectral Efficiency

0

2

120

0.2344

1

2

193

0.3770

2

2

449

0.8770

3

4

378

1.4766

4

4

490

1.9141

5

4

616

2.4063

6

6

466

2.7305

7

6

517

3.0293

8

6

567

3.3223

9

6

616

3.6094

10

6

666

3.9023

11

6

719

4.2129

12

6

772

4.5234

13

6

822

4.8154

14

6

873

5.1152

15

8

682.5

5.3320

16

8

711

5.5547

17

8

754

5.8906

18

8

797

6.2266

19

8

841

6.5703

20

8

885

6.9141

21

8

916.5

7.1602

22

8

948

7.4063

23

10

805.5

7.8662

24

10

853

8.3301

25

10

900.5

8.7939

26

10

948

9.2578

property channel_type

5G NR physical channel type. Valid choices are “PDSCH” and “PUSCH”.

check_config()[source]

Test if configuration is valid

property mcs_index

Modulation and coding scheme (MCS) index (denoted as IMCS in [3GPP38214])

property mcs_table

Indicates which MCS table from [3GPP38214] to use. Starts with “1”.

property n_id

Data scrambling initialization nID. Data Scrambling ID related to cell id and provided by higher layer. If None, the PUSCHConfig will automatically set nID=NIDcell.

Type:

int, None (default), [0, 1023]

property num_bits_per_symbol

Modulation order as defined by the selected MCS

Type:

int, read-only

property target_coderate

Target coderate of the TB as defined by the selected MCS

Type:

float, read-only

property tb_scaling

TB scaling factor for PDSCH as defined in [3GPP38214] Tab. 5.1.3.2-2

Type:

float, 1. (default), read-only

class sionna.phy.nr.TBEncoder(target_tb_size, num_coded_bits, target_coderate, num_bits_per_symbol, num_layers=1, n_rnti=1, n_id=1, channel_type='PUSCH', codeword_index=0, use_scrambler=True, verbose=False, precision=None, **kwargs)[source]

5G NR transport block (TB) encoder as defined in TS 38.214 [3GPP38214] and TS 38.211 [3GPP38211]

The transport block (TB) encoder takes as input a transport block of information bits and generates a sequence of codewords for transmission. For this, the information bit sequence is segmented into multiple codewords, protected by additional CRC checks and FEC encoded. Further, interleaving and scrambling is applied before a codeword concatenation generates the final bit sequence. Fig. 1 provides an overview of the TB encoding procedure and we refer the interested reader to [3GPP38214] and [3GPP38211] for further details.

../../_images/tb_encoding.png

Fig. 15 Fig. 1: Overview TB encoding (CB CRC does not always apply).

If n_rnti and n_id are given as list, the TBEncoder encodes num_tx = len( n_rnti ) parallel input streams with different scrambling sequences per user.

Parameters:
  • target_tb_size (int) – Target transport block size, i.e., how many information bits are encoded into the TB. Note that the effective TB size can be slightly different due to quantization. If required, zero padding is internally applied.

  • num_coded_bits (int) – Number of coded bits after TB encoding

  • target_coderate (float) – Target coderate

  • num_bits_per_symbol (int) – Modulation order, i.e., number of bits per QAM symbol

  • num_layers (1 (default) | [1,...,8]) – Number of transmission layers

  • n_rnti (int or list of int, 1 (default) | [0,…,65335]) – RNTI identifier provided by higher layer. Defaults to 1 and must be in range [0, 65335]. Defines a part of the random seed of the scrambler. If provided as list, every list entry defines the RNTI of an independent input stream.

  • n_id (int or list of int, 1 (default) | [0,…,1023]) – Data scrambling ID nID related to cell id and provided by higher layer. Defaults to 1 and must be in range [0, 1023]. If provided as list, every list entry defines the scrambling id of an independent input stream.

  • channel_type ("PUSCH" (default) | "PDSCH") – Can be either “PUSCH” or “PDSCH”.

  • codeword_index (0 (default) | 1) – Scrambler can be configured for two codeword transmission. codeword_index can be either 0 or 1. Must be 0 for channel_type = “PUSCH”.

  • use_scrambler (bool, (default True)) – If False, no data scrambling is applied (non standard-compliant).

  • verbose (bool, (default False)) – If True, additional parameters are printed during initialization.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:

inputs ([…,target_tb_size] or […,num_tx,target_tb_size], tf.float) – 2+D tensor containing the information bits to be encoded. If n_rnti and n_id are a list of size num_tx, the input must be of shape […,num_tx,target_tb_size].

Output:

[…,num_coded_bits], tf.float – 2+D tensor containing the sequence of the encoded codeword bits of the transport block.

Note

The parameters tb_size and num_coded_bits can be derived by the calculate_tb_size() function or by accessing the corresponding PUSCHConfig attributes.

property cb_crc_encoder

CB CRC encoder. None if no CB CRC is applied.

property coderate

Effective coderate of the TB after rate-matching including overhead for the CRC

Type:

float

property cw_lengths

Each list element defines the codeword length of each of the codewords after LDPC encoding and rate-matching. The total number of coded bits is cw_lengths.

property k

Number of input information bits. Equals tb_size except for zero padding of the last positions if the target_tb_size is quantized.

Type:

int

property k_padding

Number of zero padded bits at the end of the TB

Type:

int

property ldpc_encoder

LDPC encoder used for TB encoding

property n

Total number of output bits

Type:

int

property num_cbs

Number code blocks

Type:

int

property num_tx

Number of independent streams

Type:

int

property output_perm_inv

Inverse interleaver pattern for output bit interleaver.

property scrambler

Scrambler used for TB scrambling. None if no scrambler is used.

property tb_crc_encoder

TB CRC encoder

property tb_size

Effective number of information bits per TB. Note that (if required) internal zero padding can be applied to match the request exact target_tb_size.

Type:

int

class sionna.phy.nr.TBDecoder(encoder, num_bp_iter=20, cn_type='boxplus-phi', precision=None, **kwargs)[source]

5G NR transport block (TB) decoder as defined in TS 38.214 [3GPP38214].

The transport block decoder takes as input a sequence of noisy channel observations and reconstructs the corresponding transport block of information bits. The detailed procedure is described in TS 38.214 [3GPP38214] and TS 38.211 [3GPP38211].

Parameters:
  • encoder (TBEncoder) – Associated transport block encoder used for encoding of the signal

  • num_bp_iter (20 (default) | int) – Number of BP decoder iterations

  • cn_update (str, "boxplus-phi" (default) | "boxplus" | "minsum" | "offset-minsum" | "identity" | callable) – Check node update rule to be used as described above. If a callable is provided, it will be used instead as CN update. The input of the function is a ragged tensor of v2c messages of shape [num_cns, None, batch_size] where the second dimension is ragged (i.e., depends on the individual CN degree).

  • vn_update (str, "sum" (default) | "identity" | callable) – Variable node update rule to be used. If a callable is provided, it will be used instead as VN update. The input of the function is a ragged tensor of c2v messages of shape [num_vns, None, batch_size] where the second dimension is ragged (i.e., depends on the individual VN degree).

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, precision is used.

Input:

inputs ([…,num_coded_bits], tf.float) – 2+D tensor containing channel logits/llr values of the (noisy) channel observations.

Output:
  • b_hat ([…,target_tb_size], tf.float) – 2+D tensor containing hard decided bit estimates of all information bits of the transport block.

  • tb_crc_status ([…], tf.bool) – Transport block CRC status indicating if a transport block was (most likely) correctly recovered. Note that false positives are possible.

property k

Number of input information bits. Equals TB size.

Type:

int

property n

Total number of output codeword bits

Type:

int

property tb_size

Number of information bits per TB

Type:

int

Utils

sionna.phy.nr.utils.calculate_tb_size(modulation_order, target_coderate, target_tb_size=None, num_coded_bits=None, num_prbs=None, num_ofdm_symbols=None, num_dmrs_per_prb=None, num_layers=1, num_ov=0, tb_scaling=1.0, return_cw_length=True, verbose=False, precision=None)[source]

Calculates the transport block (TB) size for given system parameters

This function follows the procedure defined in TS 38.214 Sec. 5.1.3.2 and Sec. 6.1.4.2 [3GPP38214]

Input:
  • modulation_order ([…], tf.int | int) – Modulation order, i.e., number of bits per QAM symbol.

  • target_coderate ([…], tf.float | float) – Target coderate.

  • target_tb_size ([…], tf.float | float | None (default)) – Target transport block size, i.e., number of information bits that can be encoded into a slot for the given slot configuration.

  • num_coded_bits ([…], tf.int | int | None (default)) – Number of coded bits can be fit into a given slot. If provided, num_prbs, num_ofdm_symbols and num_dmrs_per_prb are ignored.

  • num_prbs ([…], tf.int | int | None (default)) – Total number of allocated PRBs per OFDM symbol, where 1 PRB equals 12 subcarriers. Must not exceed 275.

  • num_ofdm_symbols ([…], tf.int | int | None (default)) – Number of OFDM symbols allocated for transmission. Cannot be larger than 14.

  • num_dmrs_per_prb ([…], tf.int | int | None (default)) – Number of DMRS (i.e., pilot) symbols per PRB that are not used for data transmission, across all num_ofdm_symbols OFDM symbols.

  • num_layers ([…], tf.int | int (default: 1)) – Number of MIMO layers.

  • num_ov ([…], tf.int | int | None (default)) – Number of unused resource elements due to additional overhead as specified by higher layer.

  • tb_scaling ([…], tf.float | {0.25, 0.5, 1.0} (default: 1.0)) – TB scaling factor for PDSCH as defined in TS 38.214 Tab. 5.1.3.2-2.

  • return_cw_length (bool (default: True)) – If True, then the function returns tb_size, cb_size, num_cb, tb_crc_length, cb_crc_length, cw_length. Else, it does not return cw_length to reduce computation time.

  • verbose (bool, (default: False)) – If True, then additional information is printed.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, then precision is used

Output:
  • tb_size ([…], tf.int32) – Transport block (TB) size, i.e., how many information bits can be encoded into a slot for the given slot configuration

  • cb_size ([…], tf.int32) – Code block (CB) size, i.e., the number of information bits per codeword, including the TB/CB CRC parity bits

  • num_cb ([…], tf.int32) – Number of CBs that the TB is segmented into

  • tb_crc_length ([…], tf.int32) – Length of the TB CRC

  • cb_crc_length ([…], tf.int32) – Length of each CB CRC

  • cw_length ([…, N], tf.int32) – Codeword length of each of the num_cbs codewords after LDPC encoding and rate-matching. Note that zeros are appended along the last axis to obtain a dense tensor. The total number of coded bits, num_coded_bits, is the sum of cw_length across its last axis. Only returned if return_cw_length is True

sionna.phy.nr.utils.generate_prng_seq(length, c_init)[source]

Implements pseudo-random sequence generator as defined in Sec. 5.2.1 in [3GPP38211] based on a length-31 Gold sequence.

Parameters:
  • length (int) – Desired output sequence length

  • c_init (int) – Initialization sequence of the PRNG. Must be in the range of 0 to 2321.

Output:

[length], ndarray of 0s and 1s – Containing the scrambling sequence

Note

The initialization sequence c_init is application specific and is usually provided be higher layer protocols.

sionna.phy.nr.utils.decode_mcs_index(mcs_index, table_index=1, is_pusch=True, transform_precoding=False, pi2bpsk=False, check_index_validity=True, verbose=False)[source]

Returns the modulation order and target coderate for a given MCS index

Implements MCS tables as defined in [3GPP38214] for PUSCH and PDSCH.

Input:
  • mcs_index ([…], tf.int32 | int) – MCS indices (denoted as IMCS in [3GPP38214]). Accepted values are {0,1,…28}

  • table_index ([…], tf.int32 | int (default: 1)) – MCS table indices from [3GPP38214] to be used. Accepted values are {1,2,3,4}.

  • is_pusch ([…], tf.bool | bool (default: True)) – Specifies whether the 5G NR physical channel is of ‘PUSCH’ type. If False, then the ‘PDSCH’ channel is considered.

  • transform_precoding ([…], tf.bool | bool (default: False)) – Specifies whether the MCS tables described in Sec. 6.1.4.1 of [3GPP38214] are applied. Only relevant for “PUSCH”.

  • pi2bpsk ([…], tf.bool | bool | None (default)) – Specifies whether the higher-layer parameter tp-pi2BPSK described in Sec. 6.1.4.1 of [3GPP38214] is applied. Only relevant for “PUSCH”.

  • check_index_validity (bool (default: True)) – If True, an ValueError is thrown is the input MCS indices are not valid for the given configuration.

  • verbose (bool (default: False)) – If True, then additional information is printed.

Output:
  • modulation_order ([…], tf.int32) – Modulation order, i.e., number of bits per symbol, associated with the input MCS index

  • target_rate ([…], tf.float32) – Target coderate associated with the input MCS index

sionna.phy.nr.utils.calculate_num_coded_bits(modulation_order, num_prbs, num_ofdm_symbols, num_dmrs_per_prb, num_layers, num_ov, tb_scaling, precision=None)[source]

Computes the number of coded bits that fit in a slot for the given resource grid structure

Input:
  • modulation_order ([…], tf.int32) – Modulation order, i.e., number of bits per QAM symbol

  • num_prbs ([…], tf.int32 | int) – Total number of allocated PRBs per OFDM symbol, where 1 PRB equals 12 subcarriers. Must not exceed 275.

  • num_ofdm_symbols ([…], tf.int32) – Number of OFDM symbols allocated for transmission. Cannot be larger than 14.

  • num_dmrs_per_prb ([…], tf.int32) – Number of DMRS (i.e., pilot) symbols per PRB that are not used for data transmission, across all num_ofdm_symbols OFDM symbols.

  • num_layers ([…], tf.int32) – Number of MIMO layers.

  • num_ov ([…], tf.int32) – Number of unused resource elements due to additional overhead as specified by higher layer.

  • tb_scaling ([…], tf.float) – TB scaling factor for PDSCH as defined in TS 38.214 Tab. 5.1.3.2-2. Must contain values in {0.25, 0.5, 1.0}.

  • precision (None (default) | “single” | “double”) – Precision used for internal calculations and outputs. If set to None, then precision is used.

Output:

num_coded_bits ([…], tf.int | int | None (default)) – Number of coded bits can be fit into a given slot for the fiven configuration.

class sionna.phy.nr.utils.TransportBlockNR(*args, precision=None, **kwargs)[source]

Computes the number and size (measured in n. bits) of code blocks within a 5G-NR compliant transport block, given the modulation order, coderate and the total number of coded bits of a transport block. Used in PHYAbstraction. Inherits from sionna.phy.utils.TransportBlock and wraps calculate_tb_size()

Input:
  • modulation_order ([…], tf.int32) – Modulation order, i.e., number of bits per symbol, associated with the input MCS index.

  • target_rate ([…], tf.float32) – Target coderate.

  • num_coded_bits ([…], tf.float32) – Total number of coded bits across all codewords.

Output:
  • cb_size ([…], tf.int32) – Code block (CB) size, i.e., the number of information bits per code block.

  • num_cb ([…], tf.int32) – Number of code blocks that the transport block is segmented into.

class sionna.phy.nr.utils.CodedAWGNChannelNR(num_bits_per_symbol=None, num_info_bits=None, target_coderate=None, num_iter_decoder=20, cn_update_decoder='boxplus-phi', precision=None, **kwargs)[source]

Simulates a 5G-NR compliant single-link coded AWGN channel. Inherits from SingleLinkChannel

Parameters:
  • num_bits_per_symbol (int | None (default)) – Number of bits per symbol, i.e., modulation order.

  • num_info_bits (int | None (default)) – Number of information bits per code block.

  • target_coderate (float | None (default)) – Target code rate, i.e., the target ratio between the information and the coded bits within a block

  • num_iter_decoder (int (default: 20)) – Number of decoder iterations. See LDPC5GDecoder for more details.

  • cn_update ("boxplus-phi" (default) | "boxplus" | "minsum" | "offset-minsum" | "identity" | callable) – Check node update rule. See LDPC5GDecoder for more details.

  • kwargs – Additional keyword arguments for LDPC5GDecoder.

Input:
  • batch_size (int) – Size of the simulation batches

  • ebno_db (float) – Eb/No value in dB

Output:
  • bits ([batch_size, num_info_bits], int) – Transmitted bits

  • bits_hat ([batch_size, num_info_bits], int) – Decoded bits

class sionna.phy.nr.utils.MCSDecoderNR(*args, precision=None, **kwargs)[source]

Maps a Modulation and Coding Scheme (MCS) index to the corresponding modulation order, i.e., number of bits per symbol, and coderate for 5G-NR networks. Wraps decode_mcs_index() and inherits from MCSDecoder.

Input:
  • mcs_index ([…], tf.int32) – MCS index

  • mcs_table_index ([…], tf.int32) – MCS table index. Different tables contain different mappings.

  • mcs_category ([…], tf.int32) – 0 for PUSCH, 1 for PDSCH channel

  • check_index_validity (bool (default: True)) – If True, an ValueError is thrown is the input mcs indices are not valid for the given configuration.

  • transform_precoding ([…], tf.bool | bool (default: False)) – Specifies whether the MCS tables described in Sec. 6.1.4.1 of [3GPP38214] are applied. Only relevant for “PUSCH”.

  • pi2bpsk ([…], tf.bool | bool | None (default)) – Specifies whether the higher-layer parameter tp-pi2BPSK described in Sec. 6.1.4.1 of [3GPP38214] is applied. Only relevant for “PUSCH”.

  • verbose (bool (default: False)) – If True, then additional information is printed.

Output:
  • modulation_order ([…], tf.int32) – Modulation order corresponding to the input MCS index

  • target_coderate ([…], tf.float) – Target coderate corresponding to the input MCS index

References:
[3GPP38211] (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21)

3GPP TS 38.211. “NR; Physical channels and modulation.”

[3GPP38212]

3GPP TS 38.212. “NR; Multiplexing and channel coding”

[3GPP38214] (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)

3GPP TS 38.214. “NR; Physical layer procedures for data.”