PUSCH#

Physical Uplink Shared Channel algorithms in NumPy.

Overview#

The PUSCH NumPy module provides reference implementations of the complete Physical Uplink Shared Channel receiver chain. The module is organized into signal processing stages:

Inner Receiver (Signal Processing)

  • DMRS Generation and Extraction - Generate transmitted DMRS sequences and extract raw DMRS from resource grids

  • Channel Estimation - Least-squares and delay domain estimators

  • Noise Estimation - Noise covariance estimation, R-tilde estimation, and covariance shrinkage

  • Equalization - MMSE equalizer derivation, application, and combined operation

  • Soft Demapping - LLR generation from equalized symbols

  • Signal Quality Metrics - RSSI measurement, noise variance/RSRP/SINR computation, and post-equalization metrics

Outer Receiver (Channel Decoding)

  • Derate Matching - Reverse rate matching for LDPC decoder input

  • LDPC Decoding - Iterative belief propagation decoder

  • Descrambling - Bit descrambling

  • CRC Decoding - CRC validation and removal

  • Code Block Concatenation - Assemble transport blocks from code blocks

End-to-End Processing

  • Complete Receiver - Full PUSCH receiver chain

  • Inner Receiver Only - Signal processing without channel decoding

  • Outer Receiver Only - Channel decoding chain

API Reference#

PUSCH operations package.

class ran.phy.numpy.pusch.OuterRxOutputs(
tb_llr_descr: numpy.ndarray,
derate_cbs: numpy.ndarray,
nv_parity: int,
tb_cbs_est: numpy.ndarray,
num_itr: numpy.ndarray,
tb_crc_est: numpy.ndarray,
cb_err: numpy.ndarray,
tb_est: numpy.ndarray,
tb_err: int,
)[source]#

Bases: object

Outer receiver outputs.

__init__(
tb_llr_descr: numpy.ndarray,
derate_cbs: numpy.ndarray,
nv_parity: int,
tb_cbs_est: numpy.ndarray,
num_itr: numpy.ndarray,
tb_crc_est: numpy.ndarray,
cb_err: numpy.ndarray,
tb_est: numpy.ndarray,
tb_err: int,
) None#
cb_err: numpy.ndarray#
derate_cbs: numpy.ndarray#
num_itr: numpy.ndarray#
nv_parity: int#
print_debug() None[source]#

Print debug information about outer rx outputs.

tb_cbs_est: numpy.ndarray#
tb_crc_est: numpy.ndarray#
tb_err: int#
tb_est: numpy.ndarray#
tb_llr_descr: numpy.ndarray#
class ran.phy.numpy.pusch.OuterRxParams(
n_id: int,
n_rnti: int,
bgn: int,
c: int,
k: int,
f: int,
k_prime: int,
zc: int,
nl: int,
rv_idx: int,
nref: int,
g: int,
i_ls: int,
max_num_itr_cbs: int,
crc_name: str,
)[source]#

Bases: object

Parameters needed for outer rx (from test vector).

__init__(
n_id: int,
n_rnti: int,
bgn: int,
c: int,
k: int,
f: int,
k_prime: int,
zc: int,
nl: int,
rv_idx: int,
nref: int,
g: int,
i_ls: int,
max_num_itr_cbs: int,
crc_name: str,
) None#
bgn: int#
c: int#
crc_name: str#
f: int#
g: int#
i_ls: int#
k: int#
k_prime: int#
max_num_itr_cbs: int#
n_id: int#
n_rnti: int#
nl: int#
nref: int#
print_debug() None[source]#

Print debug information about outer rx parameters.

rv_idx: int#
zc: int#
ran.phy.numpy.pusch.apply_equalizer(
xtf_data: ComplexArrayNP,
w: ComplexArrayNP,
) ComplexArrayNP[source]#

Apply equalizer to TF grid for data symbols. Implements TDI=0 behavior.

Parameters:
  • xtf_data – TF grid restricted to data symbols, shape (n_f, n_sym_data, n_ant)

  • w – equalizer weights, shape (nl, n_ant, n_f)

Returns:

x_est

Return type:

estimated symbols per tone and data symbol, shape (n_f, n_sym_data, nl)

ran.phy.numpy.pusch.awgn(
H: numpy.typing.NDArray.numpy.complex64,
snr_db: float,
) numpy.typing.NDArray.numpy.complex64[source]#

Add AWGN to channel.

Parameters:
  • H – Channel with shape (n_sc, n_sym, n_ant)

  • snr_db – SNR in dB

Return type:

Noisy channel

ran.phy.numpy.pusch.channel_est_dd(
x_dmrs: ComplexArrayNP,
y_dmrs: ComplexArrayNP,
) ComplexArrayNP[source]#

Delay-domain truncation + interpolation on precomputed DMRS slices.

Parameters:
  • x_dmrs ((12*n_prb, n_sym, nl)) – Transmitted DMRS reference symbols, post scrambling & OCC (subcarrier, symbol, layer).

  • y_dmrs ((12*n_prb, n_sym, nl, n_ant)) – Received demapped DMRS symbols (subcarrier, symbol, layer, antenna).

Returns:

h_alloc

Return type:

(12*n_prb, nl, n_ant, n_dmrs_sym)

Steps:
  1. Compact LS on DMRS tones from slices.

  2. IFFT/FFT with delay-domain truncation.

  3. Interpolate to allocation (repeat along frequency).

Note: n_dmrs_sym is the number of DMRS symbols per slot, currently only supporting 1.

ran.phy.numpy.pusch.channel_est_ls(
x_dmrs: ComplexArrayNP,
y_dmrs: ComplexArrayNP,
) ComplexArrayNP[source]#

Core LS on precomputed PRB-band DMRS slices.

Parameters:
  • x_dmrs ((12*n_prb, n_sym, nl))

  • y_dmrs ((12*n_prb, n_sym, nl, n_ant))

Returns:

h_compact – Compact LS per DMRS tone (averaged across DMRS symbols).

Return type:

(6*n_prb, nl, n_ant)

ran.phy.numpy.pusch.codeblock_concatenation(
tb_cbs_est: FloatArrayNP,
c: int,
k_prime: int,
) tuple[FloatArrayNP, IntArrayNP][source]#

Remove filler and concatenate codeblocks (per-CB CRC24B removal only when C>1).

Parameters:
  • tb_cbs_est (FloatArrayNP) – Estimated codeblock bits after LDPC decoding, shape (k’, C) or larger. Each column corresponds to a codeblock, and rows correspond to bits.

  • c (int) – Number of codeblocks in the transport block.

  • k_prime (int) – Number of bits per codeblock after filler removal (including CRC24B if C > 1). Must satisfy 1 <= k_prime <= tb_cbs_est.shape[0].

Returns:

  • tb_crc_est_vec (FloatArrayNP) – Concatenated transport block bits after CRC removal (if C > 1). (column-major!) Shape is ((k_prime-24)*C,) if C > 1, or (k_prime,) if C == 1.

  • cb_err (IntArrayNP) – Per-codeblock CRC error flags. Shape is (C,) if C > 1, or (1,) if C == 1.

ran.phy.numpy.pusch.crc_decode(
tb_crc_est: FloatArrayNP,
crc_name: str = '24A',
) tuple[FloatArrayNP, int][source]#

Decode CRC-protected transport block and detect errors.

Separates the payload from the trailing CRC bits, computes the expected CRC for the payload, and compares it with the received CRC to detect errors. This implements the CRC checking procedure for 5G NR transport blocks as specified in 3GPP 38.212.

Parameters:
  • tb_crc_est – 1-D float64 array of LLRs with shape (K + ncrc,) containing the payload followed by its CRC bits. Negative values represent bit 1, non-negative values represent bit 0.

  • crc_name – CRC type identifier. Must be one of: “24A”, “24B”, “24C”, “16”, “11”. Determines the CRC length ncrc stripped from the end: - “24A”/”24B”/”24C” -> ncrc = 24 - “16” -> ncrc = 16 - “11” -> ncrc = 11

Returns:

  • tuple containing

  • - x_wo_crc (1-D float64 array with shape (K,) (CRC bits removed))

  • - err (Error flag (int scalar in {0, 1}). 0 if CRC check passes, 1 if it fails)

Notes

  • K is the number of payload LLRs (transport block without CRC).

  • ncrc is the number of CRC bits appended to the payload, determined by crc_name as listed above.

  • K + ncrc equals tb_crc_est.size; equivalently K = tb_crc_est.size - ncrc.

  • Expects tb_crc_est.ndim == 1 (no batching).

  • Requires tb_crc_est.size >= ncrc so that payload and CRC can be separated.

ran.phy.numpy.pusch.derate_match(
llr_descr: FloatArrayNP,
bgn: int,
c: int,
qam_bits: int,
k: int,
f: int,
k_prime: int,
zc: int,
nl: int,
rv_idx: int,
nref: int,
g: int,
) tuple[FloatArrayNP, int, IntArrayNP, IntArrayNP][source]#

De-rate match LLRs into codeblocks.

Implements the de-rate matching steps defined in 3GPP TS 38.212 §5.4.2 using LBMR (limited buffer mother rate), RV offsets, bit deinterleaving, bit-selection undo, filler insertion, and clamping.

Parameters:
  • llr_descr – (N?, C) float64. Descrambled LLRs arranged per codeblock (CB). The first dimension must contain at least max(E_r) per CB; only the first E_r entries in each column are consumed (where E_r is computed internally from G, C, Q_m, and N_layers).

  • bgn – int. LDPC base graph number (1 or 2).

  • c – int. Number of codeblocks (CBs) after transport-block segmentation.

  • qam_bits – int. Modulation order Q_m in bits per symbol (1=BPSK, 2=QPSK, 4=16QAM, 6=64QAM, 8=256QAM).

  • k – int. Codeblock length AFTER filler insertion, i.e., the expanded CB length used by the mother LDPC code before parity section (K). By definition K = K’ + f. Indices in this function are expressed relative to K (e.g., the filler region is [K’-2Z_c, K-2Z_c)).

  • f – int. Number of filler bits in the CB. These positions are not transmitted and are reinserted with clamped LLRs during de-rate matching. By definition f = K - K’.

  • k_prime – int. Codeblock length BEFORE filler insertion (K’). This includes the information bits for the CB plus the CB-CRC, but excludes filler bits.

  • zc – int. LDPC lifting size Z_c (submatrix dimension used to expand the base graph). It sets the mother code lengths: N = Z_c * (66 for BG1, 50 for BG2).

  • nl – int. Number of layers (N_layers) used for transmission.

  • rv_idx – int. Redundancy version index (0..3) used to compute the circular buffer read-out offset k0 for HARQ de-rate matching.

  • nref – int. Optional cap on the per-CB circular buffer length (N_ref). If > 0, n_cb = min(N, N_ref); otherwise n_cb = N. This mirrors the spec’s LBMR behavior when resources limit the read-out span.

  • g – int. Total number of coded bits G allocated to this codeword across all layers for the scheduled PDSCH/PUSCH allocation. G is used to compute the per-CB bit budgets E_r.

Returns:

  • derate_cbs – (N, C) float64. LLRs per CB after de-rate matching, including filler re-insertion and clamping, where N = Z_c * (66 or 50).

  • nv_parity – int. Number of parity variable nodes inferred for the read-out, clamped to [4, 46] for BG1 or [4, 42] for BG2 in this implementation.

  • derate_cbs_indices – (N, C) int64. One-based indices (with final offset applied) of the mother-code positions that each consumed LLR contributed to, per CB.

  • derate_cbs_sizes – (C,) int64. The number of consumed bits E_r per CB column.

Notes

  • Mother code length: N = Z_c * (66 for BG1, 50 for BG2).

  • K (k) and K’ (k_prime) differ by the filler count f = K - K’. Filler positions are not transmitted and are reconstructed by assigning clamped LLRs in [K’-2Z_c, K-2Z_c).

  • G determines the per-CB E_r through the standard split across C CBs, modulation order Q_m, and N_layers.

ran.phy.numpy.pusch.derive_equalizer(
h_est: ComplexArrayNP,
noise_intf_cov: ComplexArrayNP,
) tuple[ComplexArrayNP, FloatArrayNP][source]#

Derive per-tone MMSE-IRC equalizer and error metric.

Parameters:
  • h_est – channel estimates, shape (n_f, nl, n_ant, n_pos)

  • noise_intf_cov – noise+interference covariance, shape(n_ant, n_ant, n_prb, n_pos)

Returns:

  • w (equalizer weights, shape (nl, n_ant, n_f))

  • ree (per-layer error metric, shape (nl, n_f, n_pos))

ran.phy.numpy.pusch.descramble_bits(
llrseq: FloatArrayNP,
n_id: int,
n_rnti: int,
) FloatArrayNP[source]#

Descramble the soft-bit sequence using a Gold sequence mask.

Parameters:
  • llrseq – shape (N,) float64, concatenated LLRs across symbols

  • n_id – cell/slot ID used in seed

  • n_rnti – RNTI used in seed

Returns:

llr_descr

Return type:

shape (N,) float64, descrambled LLRs

ran.phy.numpy.pusch.embed_dmrs_ul(
r_dmrs: ComplexArrayNP,
nl: int,
port_idx: IntArrayNP,
vec_scid: IntArrayNP,
energy: float,
) ComplexArrayNP[source]#

Construct PRB-band DMRS slice for UL (allocation-local embedding).

Parameters:
  • r_dmrs (ndarray of complex128, shape (12*n_prb, n_dmrs_sym, 2)) – DMRS references already trimmed to band rows and DMRS symbols.

  • nl (int) – Number of layers.

  • port_idx (ndarray of int64, shape (nl,)) – Per-layer bitfields (bit0: fOCC, bit1: grid select, bit2: tOCC).

  • vec_scid (ndarray of int64, shape (nl,)) – Per-layer SCID selector in {0, 1}.

  • energy (float) – Energy scaling factor to apply to the embedded DMRS.

Returns:

x_dmrs – PRB-band DMRS slice. Only DMRS REs are non-zero; other tones in the PRB band are zero.

Return type:

ndarray of complex128, shape (12*n_prb, n_sym, nl)

ran.phy.numpy.pusch.equalize(
h_est: ComplexArrayNP,
noise_intf_cov: ComplexArrayNP,
xtf_data: ComplexArrayNP,
) tuple[ComplexArrayNP, FloatArrayNP][source]#

Derive and apply equalizer to TF grid for data symbols.

Parameters:
  • h_est – Channel estimate, shape (n_f, nl, n_ant, n_pos)

  • noise_intf_cov – Noise/interference covariance, shape (n_f, n_ant, n_ant)

  • xtf_data – TF grid restricted to data symbols, shape (n_f, n_sym_data, n_ant)

Returns:

  • x_est (estimated symbols per tone and data symbol, shape (n_f, n_sym_data, nl))

  • ree (per-layer error metric, shape (nl, n_f))

ran.phy.numpy.pusch.estimate_covariance(
xtf_band_dmrs: ComplexArrayNP,
x_dmrs: ComplexArrayNP,
h_est_band_dmrs: ComplexArrayNP,
rww_regularizer_val: float,
) tuple[ComplexArrayNP, FloatArrayNP][source]#

Top-level covariance pipeline from slices without sym_idx argument.

Parameters:
  • xtf_band_dmrs – Received TF grid, shape (n_prb*12, n_t_dmrs, n_ant)

  • x_dmrs – DMRS-only TF grid, shape (n_prb*12, n_t_dmrs, nl)

  • h_est_band_dmrs – Estimated channel, shape (n_prb*12, nl, n_ant, n_t_dmrs)

  • rww_regularizer_val – Regularization value for noise covariance.

Returns:

  • n_cov ((n_ant, n_ant, n_prb, n_pos))

  • mean_noise_var ((n_prb, n_pos))

Note: Infers n_pos from input shapes and computes noise covariance.

ran.phy.numpy.pusch.estimate_noise_covariance(
r_tilde: ComplexArrayNP,
rww_regularizer_val: float,
) tuple[ComplexArrayNP, FloatArrayNP][source]#

Compute per-PRB noise covariance from r_tilde without sym_idx input.

Parameters:
  • r_tilde (residuals, shape (n_prb*12, n_pos, n_ant))

  • rww_regularizer_val (regularization value for noise covariance)

Returns:

  • n_cov ((n_ant, n_ant, n_prb, n_pos))

  • mean_noise_var ((n_prb, n_pos))

ran.phy.numpy.pusch.estimate_r_tilde(
xtf_band_dmrs: ComplexArrayNP,
x_dmrs: ComplexArrayNP,
h_est_band_dmrs: ComplexArrayNP,
) ComplexArrayNP[source]#

Compute r_tilde from already-sliced PRB-band and DMRS-symbol inputs.

Parameters:
  • xtf_band_dmrs – Received TF grid, shape (n_prb*12, n_t_dmrs, n_ant)

  • x_dmrs – DMRS-only TF grid, shape (n_prb*12, n_t_dmrs, nl)

  • h_est_band_dmrs – Estimated channel, shape (n_prb*12, nl, n_ant, n_t_dmrs)

Returns:

r_tilde

Return type:

shape (n_prb*12, n_t_dmrs, n_ant)

ran.phy.numpy.pusch.extract_raw_dmrs_type_1(
xtf_band_dmrs: ComplexArrayNP,
nl: int,
port_idx: IntArrayNP,
) ComplexArrayNP[source]#

Extract received Type-1 DMRS REs only (no processing, no combining).

Parameters:
  • xtf_band_dmrs (ndarray of complex128, shape (n_f, n_dmrs_sym, n_ant)) – PRB-band received TF grid DMRS-only slice (already trimmed along time).

  • nl (int) – Number of layers.

  • port_idx (ndarray of int64, shape (nl,)) – Per-layer bitfields (bit1 selects grid: 0 even, 1 odd).

Returns:

  • y_dmrs (ndarray of complex128, shape (n_f, n_dmrs_sym, nl, n_ant)) – PRB-band slice with only DMRS REs populated per layer; others are zero.

  • Note (Assumes even number of frequency bins (i.e. subcarriers, or n_f).)

ran.phy.numpy.pusch.gen_dmrs_sym(
slot_number: int,
n_f: int,
n_dmrs_id: int,
sym_idx_dmrs: IntArrayNP | None = None,
*,
n_t: int = 14,
) tuple[ComplexArrayNP, IntArrayNP][source]#

Generate DMRS symbols and scrambling sequences.

Parameters:
  • slot_number (int) – Integer slot number

  • n_f (int) – Length of Gold sequence per port (must be even)

  • n_dmrs_id (int) – DMRS identity (integer)

  • sym_idx_dmrs (IntArrayNP | None, optional) – 0-based indices of DMRS symbols to generate. Alternatively, provide n_t (number of OFDM symbols) to generate indices [0, 1, …, n_t-1]. Exactly one of sym_idx_dmrs or n_t must be provided.

  • n_t (int, default=14) – Number of OFDM symbols

Returns:

  • r_dmrs (ComplexArrayNP) – Complex array of shape (n_f//2, n_sym, 2), with 2 = number of SCIDs

  • scr_seq (IntArrayNP) – Integer array of shape (n_f, n_sym, 2)

ran.phy.numpy.pusch.ldpc_decode(
derate_cbs: FloatArrayNP,
nv_parity: int,
zc: int,
c: int,
bgn: int,
i_ls: int,
max_num_itr_cbs: int,
) tuple[FloatArrayNP, IntArrayNP][source]#

Run layered min-sum LDPC decoding per 3GPP TS 38.212.

Parameters:
  • derate_cbs – LLRs after de-rate match shaped (N, C), where C is the number of codeblocks and N depends on zc and the graph size.

  • nv_parity – int, Number of parity variable nodes inferred by de-rate matching.

  • zc – int, Lifting size (Zc) of the LDPC base graph.

  • c – int, Number of codeblocks in the transport block.

  • bgn – int, Base graph number (1 or 2).

  • i_ls – Layer-shift index to select the parity-check permutation table.

  • max_num_itr_cbs – int, Maximum iterations per codeblock

Returns:

  • tb_out (FloatArrayNP, Decoded systematic bits as float64 in shape) – (nV_sym*zc, C), matched to MATLAB’s column-major layout.

  • num_itr (IntArrayNP, Iterations used per codeblock, shape (C,).)

ran.phy.numpy.pusch.measure_rssi(
xtf_band_dmrs: ComplexArrayNP,
) tuple[FloatArrayNP, numpy.float64][source]#

Compute DMRS RSSI metrics.

Parameters:

xtf_band_dmrs (ndarray of shape (n_f_alloc, n_dmrs, n_ant)) – PRB-band slice over the DMRS symbols for all antennas.

Returns:

  • rssi_db (per-symbol, per-antenna RSSI in dB, shape (n_dmrs, n_ant))

  • rssi_reported_db (aggregated RSSI across symbols and antennas (dB), shape ())

ran.phy.numpy.pusch.ncov_shrinkage(n_cov: ComplexArrayNP) ComplexArrayNP[source]#

Apply optional shrinkage to n_cov without mutating the input.

Parameters:

n_cov – Noise covariance, shape (n_ant, n_ant, n_prb, n_pos)

Return type:

Covariance array. Shape (n_ant, n_ant, n_prb, n_pos)

ran.phy.numpy.pusch.noise_rsrp_sinr_db(
mean_noise_var: FloatArrayNP,
h_est: ComplexArrayNP,
layer2ue: IntArrayNP,
n_ue: int,
) tuple[FloatArrayNP, FloatArrayNP, FloatArrayNP][source]#

Compute noise variance, RSRP, and SINR per DMRS position and UE (all in dB).

This function sequentially calls noise_variance_db, rsrp_db, and sinr_db to compute all three metrics in one call.

Parameters:
  • mean_noise_var – Noise variance estimate per DMRS position, shape (n_prb, n_pos)

  • h_est – Channel estimates, shape (n_f, nl, n_ant, n_pos)

  • layer2ue – Maps layers to UEs (0-indexed), shape (nl,)

  • n_ue – Number of UEs

Returns:

  • tuple containing

  • - noise_var_db (Noise variance in dB, shape (n_pos,))

  • - rsrp_db_val (RSRP in dB, shape (n_pos, n_ue))

  • - sinr_db_val (SINR in dB, shape (n_pos, n_ue))

ran.phy.numpy.pusch.noise_variance_db(mean_noise_var: FloatArrayNP) FloatArrayNP[source]#

Compute noise variance per DMRS position (dB) following detPusch.m.

Parameters:

mean_noise_var – (n_prb, n_pos) linear noise variances

Return type:

noise variance in dB, shape (n_pos,)

ran.phy.numpy.pusch.post_eq_noisevar_sinr(
ree: FloatArrayNP,
layer2ue: IntArrayNP,
n_ue: int,
) tuple[FloatArrayNP, FloatArrayNP][source]#

Compute post-equalization noise variance and SINR in dB.

Parameters:
  • ree – Per-layer, per-tone, per-DMRS Ree over the allocated band; shape (nl, n_f_alloc, n_sym)

  • layer2ue – mapping from layer index to UE index (0-based), shape (nl,)

  • n_ue – number of UEs

Returns:

  • post_eq_noise_var_db (shape (n_sym, n_ue))

  • post_eq_sinr_db (shape (n_sym, n_ue))

ran.phy.numpy.pusch.pusch_inner_rx(
inputs: dict[str, Any],
) dict[str, Any][source]#

Run the reference PUSCH Rx pipeline end-to-end from a unified dict.

  • Inputs are auto-trimmed per block; only required kwargs are passed.

  • Outputs from prior blocks are injected via small overrides.

ran.phy.numpy.pusch.pusch_outer_rx(
llr__time_allocfreq_layer_qambit: numpy.ndarray,
outer_rx_params: OuterRxParams,
qam_bits: int,
) OuterRxOutputs[source]#

Apply outer rx blocks to LLRs from optimized PUSCH Rx.

This function takes the LLR output from the JAX optimized pusch inner rx and applies the reference outer rx blocks to decode the transport block.

Parameters:
  • llr__time_allocfreq_layer_qambit – LLRs in column-major format (n_datasym, n_allocsc, n_layer, qam_bits)

  • outer_rx_params – Backend processing parameters from test vector

  • qam_bits – QAM modulation order (bits per symbol)

Returns:

Decoded transport block and intermediate outputs

Return type:

OuterRxOutputs

ran.phy.numpy.pusch.pusch_rx(inputs: dict[str, Any]) dict[str, Any][source]#

Run the reference PUSCH Rx pipeline end-to-end from a unified dict.

  • Inputs are auto-trimmed per block; only required kwargs are passed.

  • Outputs from prior blocks are injected via small overrides.

ran.phy.numpy.pusch.rsrp_db(
h_est: ComplexArrayNP,
layer2ue: IntArrayNP,
n_ue: int,
) FloatArrayNP[source]#

Compute RSRP per DMRS position and UE (dB) following detPusch.m.

Parameters:
  • h_est – (n_f, nl, n_ant, n_pos) complex channel estimates, with nf: number of subcarriers in frequency (12*n_prb)

  • layer2ue – (nl,) int mapping from layer index to UE index (0-based)

  • n_ue – number of UEs

Return type:

rsrp in dB, shape (n_pos, n_ue)

ran.phy.numpy.pusch.sinr_db(
rsrp_db: FloatArrayNP,
noise_var_db: FloatArrayNP,
) FloatArrayNP[source]#

Compute SINR per DMRS position and UE (dB) as rsrp_db - noise_var_db.

Parameters:
  • rsrp_db – (n_pos, n_ue) RSRP in dB

  • noise_var_db – (n_pos,) noise variance in dB

Return type:

SINR in dB, shape (n_pos, n_ue)

ran.phy.numpy.pusch.soft_demapper(
x: ComplexArrayNP,
ree: FloatArrayNP,
qam_bits: int,
) FloatArrayNP[source]#

Simplified cuPHY-style soft demapper (vectorized), 100%-matching tables.

Parameters:
  • x – estimated symbols on allocated tones, shape (n_f_alloc, n_sym, n_layers)

  • ree – noise variance per layer/tone (optional sym axis), shape (n_layers, n_f_alloc) or (n_layers, n_f_alloc, 1)

  • qam_bits – QAM modulation order (number of bits per symbol). One of {1, 2, 4, 6, 8}.

Return type:

LLR tensor of shape (qam_bits, nl, n_f_alloc, n_sym) = (qam_bits, L, F, T)