HARQ

The module harq.py contains the API for Hybrid Automatic Repeat reQuest (HARQ), a fundamental mechanism in 5G NR, designed for error correction and reliable data transfer over radio links. The class structure follows the 3GPP HARQ hierarchy:

  • HarqEntity: The main HARQ management object. It implements the “HARQ Entity” as specified in 3GPP TS 38.321, Section 5.3.2.1.

  • HarqProcess: Each HARQ entity contains one or more HARQ processes. This class implements the “HARQ Process” object as specified in 3GPP TS 38.321, Section 5.3.2.2.

  • HarqCW: Each HARQ process can handle up to two codewords. This class implements HARQ processing for a single codeword. A HarqProcess can have one or two HarqCW objects, depending on the number of codewords.

This implementation adheres to the procedures specified in 3GPP TS 38.321, TS 38.212, and TS 38.214.

A typical HARQ workflow:

Here’s a typical workflow example for using HARQ in your simulations:

  1. Create an LDPC encoder, for instance:

    ldpcEncoder = LdpcEncoder(baseGraphNo=1, modulation='QPSK', txLayers=1, targetRate=490/1024)
    
  2. Create a HARQ entity object, passing in the LDPC encoder:

    harq = HarqEntity(ldpcEncoder, harqType="IR", numProc=16)  # Using "Incremental Redundancy" with 16 HARQ processes
    
  3. For each transmission and each codeword, check the needNewData property. If it is True for a codeword, create the transport block bits for the corresponding codeword. Otherwise, set the transport block to None (for retransmission). Once the transport blocks are ready for each codeword, call the HarqEntity.getRateMatchedCodeBlocks() function to prepare the rate-matched bitstreams for transmission. Here is an example:

    txBlocks = []                                         # Transport blocks, one per codeword.
    for c in range(numCodewords):
        if harq.needNewData[c]:                           # New transmission.
            txBlocks += [ random.bits(txBlockSizes[c]) ]  # Create random bits for the new transport block
        else:                                             # Retransmission
            txBlocks += [ None ]                          # Set transport block to None indicating retransmission
    
    rateMatchedCodeWords = harq.getRateMatchedCodeBlocks(txBlocks)  # Prepare the bitstream for transmission
    
  4. At the receiving end, after demodulating the LLRs, the HarqEntity.decodeLLRs() method is invoked to obtain a list of decoded blocks for each codeword. For instance:

    decodedTxBlocks, blockErrors = harq.decodeLLRs(llrs, txBlockSize)  # Decode received LLRs
    
  5. Near the end of the transmission loop, the HARQ entity’s goNext() method is called to transition to the next HARQ process for the subsequent transmission.

    harq.goNext()
    

Please refer to the notebook Using HARQ for a complete example of the above workflow.

class neoradium.harq.HarqCW(process, cwIdx)

This class implements the HARQ Processing for a single codeword. The neoradium.harq.HarqProcess class can have either one or two HarqCW objects.

Parameters:
  • process (HarqProcess) – The HarqProcess object that holds this HarqCW instance.

  • cwIdx (int) – The codeword index, which is 0 for the first codeword, or 1 for the second codeword.

Other Read-Only Properties:

curTry:

This indicates the current number of retransmissions. It starts at zero for the first transmission for this codeword. This value increments after each transmission failure.

rv:

This represents the redundancy version used for retransmissions. For “Chase Combining” retransmission, this value is always zero. For “Incremental Redundancy” retransmission, it is updated based on the rvSequence parameter of the HarqEntity object.

txBlockNo:

This indicates the number of unique transport blocks (not including retransmissions) transmitted so far since the start of communication before the one currently being processed by this HarqCW instance.

needNewData:

This indicates whether the HarqCW object is ready to receive a new transport block for transmission. If it is True, it means it is ready to transmit the new block. If it is False, it means it is still busy retransmitting the previous block. This corresponds to the New Data Indicator (NDI) in 3GPP TS 38.212

class neoradium.harq.HarqProcess(entity, id, numCW)

This class encapsulates the functionality of a HARQ process as outlined in 3GPP TS 38.321, Section 5.3.2.2. A HARQ entity manages multiple parallel HARQ processes, each identified by a unique HARQ process identifier. The UE capabilities determine the maximum number of HARQ processes per cell, which can be 16 or 32. A single HARQ process can support one or two Transport Blocks, depending on the number of codewords. In this implementation, each codeword is processed by a dedicated HarqCW object.

Parameters:
  • entity (HarqEntity) – The HarqEntity object that holds this HarqProcess instance.

  • id (int) – The unique identifier associated with this HARQ process.

  • numCW (int) – The number of codewords processed by this HARQ process. It can be 1 or 2.

Other Read-Only Properties:

needNewData:

A list of one or two boolean values, corresponding to each codeword. For each element in the list, a True value indicates that the HARQ process is ready to receive a new transport block for transmission, while a False value signifies that it is currently busy retransmitting the previous transport block. This aligns with the New Data Indicator (NDI) defined in 3GPP TS 38.212.

reset()

Resets this HARQ process to prepare it for new transmissions. It resets the counters and releases any encoder/decoder retransmission buffers by calling the reset method of the HarqCW objects.

print(indent=0, title=None, getStr=False)

Prints the properties of this HarqProcess object and its HarqCW objects.

Parameters:
  • indent (int) – The number of indentation characters.

  • title (str) – If specified, it is used as a title for the printed information.

  • getStr (Boolean) – If True, returns a text string instead of printing it.

Returns:

If the getStr parameter is True, then this function returns the information in a text string. Otherwise, nothing is returned.

Return type:

None or str

getRateMatchedCodeBlocks(txBlocks, gs=None, concatCBs=True)

This function takes a list of one or two transport blocks (txBlocks) based on the number of codewords and returns a list of LDPC-encoded and rate-matched bitstreams for each codeword. If a transport block in the txBlocks list is set to None, it uses the buffered encoded bitstream and only applies rate matching for retransmitting the previously encoded transport block. Otherwise, it assumes new transmission and encodes the transport block, saving the encoded bits for future retransmissions. For more information about the LDPC encoding process, refer to the getRateMatchedCodeBlocks() method of the LdpcEncoder class.

Parameters:
  • txBlocks (list) – A list of one or two NumPy arrays for each codeword. The presence of a ‘None’ value for each transport block in the list indicates a retransmission of previously buffered encoded transport block.

  • g (list or None) – A list of one or two integer values for each codeword. Each element in the list represents the total number of bits available for transmitting the transport block. This value corresponds to the value \(G\) in the bit selection process explained in 3GPP TS 38.212, Section 5.4.2.1. If not provided (default), it is calculated using the formula \(G=\lceil \frac {B-24} R \rceil\), where \(B\) is the transport block size and \(R\) is the code rate.

  • concatCBs (Boolean) – If True (Default), the rate-matched code blocks are concatenated, and a single array of bits is returned for each codeword. Otherwise, for each codeword, a list of NumPy arrays is returned, where each element in the list represents the bit array corresponding to each code block.

Returns:

If concatCBs is True, a one-dimensional NumPy array is returned, containing the concatenation of all rate-matched coded blocks. Otherwise, a list of NumPy arrays is returned, where each element in the list corresponds to the bit array of a coded block.

Return type:

NumPy array or list of NumPy arrays

decodeLLRs(llrs, txBlockSizes, numIter=5)

This function takes a list of one or two NumPy arrays, each containing Log-Likelihood Ratios (LLRs) for the demodulated received signals for each codeword. It then returns a list of one or two decoded transport blocks, with the size of each transport block specified by the txBlockSizes list, which provides the transport block sizes for each codeword.

For each codeword, the function recovers the rate, decodes the code blocks using LDPC decoding, checks the code block CRCs, and merges all code blocks to reassemble the transport block. In case of retransmissions, the function combines the LLR values of the retransmission with those from previous transmissions before decoding the code blocks.

Parameters:
  • llrs (list) – A list of one or two NumPy arrays, each containing the Log-Likelihood Ratios (LLRs) from the demodulated received signals corresponding to each codeword.

  • txBlockSizes (list) – A list containing one or two integer values, each representing the transport block size for each codeword.

  • numIter (int) – The number of iterations in the Layered Belief Propagation decoding algorithm. In some cases, larger values may lead to more accurate decoding but can slow down the entire decoding process. For more information, please refer to the decode() method of the LdpcDecoder class. The default value is 5.

Returns:

  • decodedTxBlock (list) – A list of one or two NumPy arrays, each containing the decoded transport blocks for each codeword.

  • blockErrors (list) – A list containing one or two integer values, each representing the number of code block CRC errors for the corresponding codeword.

class neoradium.harq.HarqEntity(encoder, harqType='CC', numProc=8, rvSequence=[0, 2, 3, 1], maxTries=4, eventCallback=None)

This class encapsulates the functionality of a HARQ entity as specified in 3GPP TS 38.321, Section 5.3.2.1. A HARQ entity is configured for each Serving Cell to manage downlink (DL) and uplink (UL) HARQ operations. The primary purpose of a HARQ entity is to maintain multiple parallel HARQ processes and direct HARQ information and associated Transport Blocks to these corresponding processes. This HARQ information includes key parameters such as the New Data Indicator (NDI), Transport Block Size (TBS), Redundancy Version (RV), and the HARQ process ID.

Parameters:
  • encoder (LdpcEncoder) – The neoradium.ldpc.LdpcEncoder object, used by this HARQ entity for LDPC encoding. It is also used to create the corresponding neoradium.ldpc.LdpcDecoder object, which is used for LDPC decoding.

  • harqType (str) –

    The retransmission method used by this HARQ entity. It can be one of "CC"``(default) or ``"IR":

    ”CC”:

    Indicates Chase Combining which is a straightforward HARQ method where each retransmission is an exact copy of the original data. The receiver simply combines the energy (or LLRs) from these identical transmissions, which increases the signal-to-noise ratio and makes it more likely that the combined signal can be decoded correctly.

    ”IR”:

    Indicates Incremental Redundancy, a more efficient and advanced method. Instead of re-sending identical copies, each retransmission contains new and different parity bits, known as redundancy versions. The receiver combines these unique pieces of information with the original transmission, progressively building a stronger and more complete coded block. This approach significantly improves the chances of successful decoding with fewer retransmissions compared to Chase Combining.

    numProc: int

    The number of HARQ processes utilized by this HARQ entity. It varies depending on the capabilities of the UE. A HARQ entity can manage up to 32 HARQ processes, with the default being 8.

    rvSequence: list of integers

    For the Incremental Redundancy HARQ, this specifies the sequence of redundancy versions (RV) values used for each consecutive retransmission. The default sequence is [0, 2, 3, 1], which is based on 3GPP TS 38.214, Table 5.1.2.1-2.

    maxTries: int

    The maximum number of transmission attempts for a specified transport block. If a transport block’s transmission fails after this many attempts, a timeout event occurs. In the case of PDSCH, this value is equivalent to the parameter pdsch-AggregationFactor, as explained in 3GPP TS 38.321, Section 5.3.2.1.

    eventCallback: function or None

    If this callback function is provided, it will be invoked on the following events:

    “RXFAILED”:

    This event is triggered when a transport block transmission fails, whether it’s the original transmission or a retransmission.

    “RXSUCCESS”:

    This event is triggered when a transport block transmission succeeds, whether it’s the original transmission or a retransmission.

    “TIMEOUT”:

    This event is triggered when a transport block transmission fails after the maxTries transmissions.

    For more information, refer to the Event callback function section below.

Other Read-Only Properties:

processes:

A list of HarqProcess objects managed by this HARQ entity.

curProcess:

The HARQ process that is currently transmitting or retransmitting the transport blocks.

curProcIdx:

The current HARQ process that is currently transmitting or retransmitting the transport blocks.

numCW:

The number of codewords. This is based on the LDPC encoder’s txLayers parameter.

needNewData:

A list of one or two boolean values, corresponding to each codeword. For each element in the list, a True value indicates that current HARQ process (curProcess) is ready to receive a new transport block for transmission, while a False value signifies that it is currently busy retransmitting the previous transport block. This aligns with the New Data Indicator (NDI) defined in 3GPP TS 38.212.

totalTxBlocks:

The total number of transport blocks transmitted, including retransmissions.

totalRxBlocks:

The total number of transport blocks received and successfully decoded.

totalTxBits:

The total number of transport block bits transmitted, including retransmissions.

totalRxBits:

The total number of transport block bits received and successfully decoded.

throughput:

The communication throughput expressed as a percentage. It’s calculated as totalRxBits*100/totalTxBits.

bler:

The Block Error Rate expressed as a percentage. It’s calculated as (totalTxBlocks-totalRxBlocks)*100/totalTxBlocks.

numTimeouts:

The total number of timeout events, which occur when a transport block fails after all retransmissions attempts.

meanTries:

The average number of transmission attempts per transport block. This value ranges from zero (indicating no retransmissions) to maxTries (indicating all transmissions timed out).

Event Callback Function

This function is automatically invoked when a transmission event occurs. It accepts the following parameters:

eventStr:

This string can be one of ”RXFAILED”, ”RXSUCCESS”, or ”TIMEOUT”, as explained above.

harqCW:

The HarqCW object that triggered the event. This object can be used to obtain more information about the event.

Here is an example of an event callback function that prints all triggered events:

def myEventHandler(eventStr, harqCW):
    print(f"HARQ Process {harqCW.process.id:2d} CW{harqCW.cwIdx+1}:{event:10s} curTry:{harqCW.curTry} RV:{harqCW.rv}")

Please refer to the notebook HARQ event callback for a complete example of using event callback functions.

reset()

Resets this HARQ entity to prepare it for a new set of transmissions. It resets the counters and releases any encoder/decoder retransmission buffers by invoking HarqProcess.reset() for all HARQ processes.

print(indent=0, title=None, getStr=False)

Prints the properties of this HarqEntity object.

Parameters:
  • indent (int) – The number of indentation characters.

  • title (str) – If specified, it is used as a title for the printed information.

  • getStr (Boolean) – If True, returns a text string instead of printing it.

Returns:

If the getStr parameter is True, then this function returns the information in a text string. Otherwise, nothing is returned.

Return type:

None or str

printStats(getStr=False)

Prints the statistics of this HarqEntity object.

Parameters:

getStr (Boolean) – If True, returns a text string instead of printing it.

Returns:

If the getStr parameter is True, then this function returns the information in a text string. Otherwise, nothing is returned.

Return type:

None or str

goNext()

This function should be called after the transmission of each transport block. It updates the internal pointer curProcIdx to point to the next HARQ process.

getRateMatchedCodeBlocks(txBlocks, gs=None, concatCBs=True)

This function takes a list of one or two transport blocks (txBlocks) based on the number of codewords and returns a list of LDPC-encoded and rate-matched bitstreams for each codeword. If a transport block in the txBlocks list is set to None, it uses the buffered encoded bitstream and only applies rate matching for retransmitting the previously encoded transport block. Otherwise, it assumes new transmission and encodes the transport block and saves the encoded bits into the HARQ process for future retransmissions. This function internally calls the getRateMatchedCodeBlocks() method of the HarqProcess class. For more details, refer to the documentation of HarqProcess.getRateMatchedCodeBlocks().

decodeLLRs(llrs, txBlockSize, numIter=5)

This function takes a list of one or two NumPy arrays, each containing Log-Likelihood Ratios (LLRs) for the demodulated received signals for each codeword. It then returns a list of one or two decoded transport blocks, with the size of each transport block specified by the txBlockSizes list, which provides the transport block sizes for each codeword.

For each codeword, the function recovers the rate, decodes the code blocks using LDPC decoding, checks the code block CRCs, and merges all code blocks to reassemble the transport block. In case of retransmissions, the function combines the LLR values of the retransmissions with those from previous transmissions before decoding the code blocks.

This function internally calls the decodeLLRs() method of the HarqProcess class. For more details, refer to the documentation of HarqProcess.decodeLLRs().