Merge remote-tracking branch 'alexjiao2021/fix-issue-216' into integration_2026_w26

Fix for issue 216 (#221)

When O_ACK <= 2, the reserved HARQ-ACK positions are selected from the
codeword using a d-factor stepping algorithm. The d-factor was computed
and applied at bit granularity, causing it to step through the flat
positions array mid-modulation-group when Qm > 1.

This scattered ACK/placeholder bits across more REs than intended,
mismatching the RE selection of the demultiplexer which operates at RE
granularity. As a result, placeholder positions were marked on REs that
the gNB treats as pure ULSCH, causing those positions to be transmitted
without the normal scrambling sequence XOR. This corrupts the ULSCH soft
bits at the gNB side, leading to PUSCH CRC failure.

Fix: compute the d-factor in RE units and step through the positions
array in strides of d_factor_re * Qm so that whole Qm-bit modulation
groups are always selected or skipped together.

Observed with O_ACK=1, MCS27 (256QAM), beta_offset_idx=11 where
Q_dash_ACK=6 (reserved) vs Q_dash_ACK_actual=3, giving d_factor=2. PUSCH
CRC was KO at third-party signal analyzers (MATLAB/WaveJudge) while the
OCUDU gNB could still decode due to soft LDPC margin.

Closes: #216
Reviewed-by: Francesco Mani <email@francescomani.it>
Reviewed-by: Sakthivel Velumani <s.velumani@northeastern.edu>
This commit is contained in:
Robert Schmidt
2026-06-25 11:41:31 +02:00

View File

@@ -882,15 +882,19 @@ static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
const int32_t num_ack_remaining = G_ack - ack_bits_marked;
if (num_ack_remaining <= 0)
continue;
const uint32_t d_factor_re = get_d_factor_re(num_ack_remaining, num_reserved_bits_on_sym);
for (uint32_t i = 0; i < num_reserved_bits_on_sym && ack_bits_marked < G_ack; i += d_factor_re) {
uint32_t pos = reserved_indices_on_this_sym[i];
int bit_in_group = pos % Qm;
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
else // puncturing CSIp2
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER_CSI2 : BIT_TYPE_ACK_RESERVED_CSI2;
ack_bits_marked++;
const uint32_t num_reserved_re = num_reserved_bits_on_sym / Qm;
const uint32_t num_ack_re_remaining = num_ack_remaining / Qm;
const uint32_t d_factor_re = get_d_factor_re(num_ack_re_remaining, num_reserved_re);
for (uint32_t re = 0; re < num_reserved_re && ack_bits_marked < G_ack; re += d_factor_re) {
for (int b = 0; b < Qm; b++) {
uint32_t pos = reserved_indices_on_this_sym[re * Qm + b];
int bit_in_group = pos % Qm;
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
else // puncturing CSIp2
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER_CSI2 : BIT_TYPE_ACK_RESERVED_CSI2;
ack_bits_marked++;
}
}
}
}