Main Content

Generate CU-Plane Messages for O-RAN Fronthaul Test

This example shows how to generate fronthaul control and user (CU) plane messages for open radio access network (O-RAN) conformance tests using 5G Toolbox™. These O-RAN compliant messages are considered as split option 7.2x test vectors generated from an O-RAN distributed unit (O-DU). The example generates a packet capture (PCAP) file that contains these messages.

Introduction

This example shows how to build the O-RAN fronthaul CU-plane messages, as defined in TS O-RAN.WG4.CUS, which transmit an NR test model waveform, as defined in TS 38.141-1. These O-RAN compliant messages are considered as split option 7.2x test vectors generated from an O-DU. The example generates a PCAP file, which contains the messages. You can use the generated packets to test an O-RAN radio unit (O-RU) following the conformance test specifications in TS O-RAN.WG4.CONF. You can also analyze the generated PCAP file with third-party packet analysis tools. In this example, Wireshark is used to verify that the content of the CU-plane messages is as expected.

workflow5.png

This example builds the CU-plane messages required to transmit the specified NR test model waveform. The example generates a single C-plane message per slot, using Section Type 1, and a single U-plane message per symbol. The U-plane messages encapsulate the IQ data using a single section per symbol. The following diagram shows the flow of the generated CU-plane messages.

GenerateCUPlaneMessagesForORANFronthaulTestPacketGeneration.png

Set Configuration Parameters

The data frame to transmit consists of a full band 5G NR test model waveform, as defined in TS 38.141-1. You can set the test model, the channel bandwidth, and the subcarrier spacing of the test waveform. This example supports FDD duplex mode only and does not apply any precoding or beamforming.

The generated PCAP file includes the Ethernet, eCPRI, and O-RAN protocols. In this section, you can configure parameters available in the three protocols:

  • O-RAN — Set the compression method and the IQ samples bit-width before and after compression.

  • eCPRI — Set the extended antenna-carrier identifier (eAxC ID) fields. All packets have the same eAxC ID because this example supports only a single stream.

  • Ethernet — Set the Ethernet VLAN tag, and the MAC source and destination addresses.

This example does not generate management plane (M-plane) messages. However, the example enables you to set these M-plane parameters: compression mode, byte order, and number of bits per field in eAxC ID.

% Select the NR test waveform parameters
waveConfig.tm = "NR-FR1-TM1.1";  % Test model (must be full band)
waveConfig.bw = "100MHz";  % Channel bandwidth in MHz
waveConfig.scs = "30kHz"; % Subcarrier spacing in kHz

% O-RAN configuration
% Select the compression parameters
oranConfig.method = 'BFP'; % Compression method
oranConfig.IQWidth = 16;                  % IQ samples bit-width before compression
oranConfig.cIQWidth = 9;                  % Compressed IQ samples bit-width (1 to 16)

% eCPRI configuration
% Select the eAxC ID fields
eCPRIConfig.DUPortID = 0;      % Distributed unit identifier
eCPRIConfig.BandSectorID = 0;  % Band and sector identifier
eCPRIConfig.CCID = 0;          % Component carrier identifier
eCPRIConfig.RUPortID = 0;      % Spatial stream identifier

% Ethernet configuration
% Select the Ethernet VLAN tag
ethernetConfig.TPID = 0x8100; % Tag protocol identifier
ethernetConfig.priority = 7;                % Priority level (0 to 7)
ethernetConfig.DEI = 0;  % Drop eligible indicator
ethernetConfig.VID = 1;                     % Unique VLAN identifier (0 to 4095)
% Select the MAC source and destination addresses
ethernetConfig.sourceAddress = ["56","3b","be","a9","92","4c"]; % MAC source address
ethernetConfig.destAddress = ["da","14","de","b0","55","63"];   % MAC destination address

% Select the M-plane parameters
mPlaneConfig.compMode = 0;  % Compression mode
mPlaneConfig.byteOrder = 0; % Byte order
mPlaneConfig.eAxCIDBits = [2 6 4 4];           % Number of bits per field in eAxC ID

% Set the name of the PCAP file
pcapFileName = 'CUMessages'; % PCAP file name

Generate U-Plane Data

Use the hNRReferenceWaveformGenerator class to generate the complete 5G NR configuration of the frame that you selected in the previous section. The resource grid of the NR frame (split option 7.2x) is the data carried in the U-plane messages.

% Create waveform generator object
waveConfig.dm = "FDD"; % FDD duplexing mode
tmWaveGen = hNRReferenceWaveformGenerator(waveConfig.tm,waveConfig.bw, ...
    waveConfig.scs,waveConfig.dm);

% Generate waveform and get resource grid of one frame of data
[~,gridSet] = generateWaveform(tmWaveGen);
grid = gridSet.ResourceGridBWP;

Compress U-Plane Data

Next, compress the generated resource by using the nrORANBlockCompress function. Compression method options include block floating point (BFP), block scaling, and mu-law, as defined in TS O-RAN.WG4.CUS Annex A.1.1, A.2.1, and A.3.1, respectively. Before applying the compression, scale the IQ samples in the resource grid to the bit-width specified by oranConfig.IQWidth.

% Scale the IQ samples in the resource grid using IQWidth
peak = max(abs([real(grid(:)); imag(grid(:))]));
scaleFactor = peak / (0.95*(2^(oranConfig.IQWidth-1)-1));
oranConfig.scaledGrid = round(grid/scaleFactor);

% Apply compression to the scaled resource grid if selected
if ~strcmp(oranConfig.method,'No compression')
    [oranConfig.cGrid,oranConfig.cParam] = nrORANBlockCompress(oranConfig.scaledGrid, ...
        oranConfig.method,oranConfig.cIQWidth,oranConfig.IQWidth);
end

Extract CU-Plane Message Configuration

Use the getCUMessagesConfig local function to get the configuration per CU-plane message:

  • Ethernet header

  • eCPRI header

  • eCPRI payload (O-RAN configuration)

% Get the number of symbols per slot and the number of slots per subframe
waveConfig.symbolsPerSlot = gridSet.Info.SymbolsPerSlot;
waveConfig.slotsPerSubframe = gridSet.Info.SlotsPerSubframe;  

% Obtain set of CU-plane messages 
[cMessagesConfig,uMessagesConfig] = getCUMessagesConfig(waveConfig,oranConfig, ...
    eCPRIConfig,ethernetConfig);

Generate CU-Plane Messages

The generateCUMessages local function generates the CU-plane messages according to their configuration in cMessagesConfig and uMessagesConfig, respectively, and writes the messages to a PCAP file. The generated C-plane messages are Section Type 1, see TS O-RAN.WG4.CUS section 5.4.

% Generate CU-plane messages and write the packets to PCAP file
generateCUMessages(cMessagesConfig,uMessagesConfig,mPlaneConfig,pcapFileName);

Visualize Generated CU-Plane Messages

You can open the PCAP file containing the generated CU-plane messages in a packet analyzer. The packets decoded by Wireshark match the configuration selected. These figures show the analysis of two captured CU-plane messages in Wireshark.

First C-Plane Message

GenerateCUPlaneMessagesForORANFronthaulTestCPlaneData.png

First U-Plane Message

GenerateCUPlaneMessagesForORANFronthaulTestUPlaneData.png

References

  1. 3GPP TS 38.141-1. "NR; Base Station (BS) conformance testing Part 1: Conducted conformance testing." 3rd Generation Partnership Project; Technical Specification Group Radio Access Network.

  2. TS O-RAN.WG4.CUS. "O-RAN Fronthaul Working Group - Control, User and Synchronization Plane Specification".

  3. TS O-RAN.WG4.CONF. "O-RAN Fronthaul Working Group - Conformance Test Specification".

  4. Wireshark: https://www.wireshark.org/. Accessed 13 July 2022.

Local Functions

function [cMessagesConfig,uMessagesConfig] = getCUMessagesConfig(waveConfig,oranConfig,eCPRIConfig,ethernetConfig)
% Obtain CU-plane messages configuration. This function considers Section Type 1 C-plane messages.

    % Get the following information per PRB:
    % - PRBNumber: PRB number
    % - cParam: computational parameter specific to the compression method
    % - cIQWidth: compressed IQ samples bit-width (same as IQWidth if uncompressed)
    % - IQData: IQ samples

    K = size(oranConfig.scaledGrid,1);
    L = size(oranConfig.scaledGrid,2);
    if ~strcmp(oranConfig.method,'No compression')
        grid = oranConfig.cGrid;
        cParam = oranConfig.cParam;
        cIQWidth = oranConfig.cIQWidth;
    else
        grid = oranConfig.scaledGrid;
        cParam = NaN(K/12,L);
        cIQWidth = oranConfig.IQWidth;
    end
    PRBsInfo = struct;
    if length(cIQWidth) == 1
        cIQWidth = repmat(cIQWidth,[K/12 L]);
    end
    for l = 1:L
        PRBNumber = 0;
        for k = 1:K/12
            PRBsInfo(k,l).PRBNumber = PRBNumber;  
            PRBsInfo(k,l).CompParam = [cParam(k,l) cIQWidth(k,l)];
            PRBsInfo(k,l).IQData = grid((k-1)*12+1:k*12,l);
            PRBNumber = PRBNumber+1;
        end
    end
    
    % Get time configuration of first U-plane message
    uTimeInfo.DataDirection = 1;
    uTimeInfo.PayloadVersion = 1;
    uTimeInfo.FilterIndex = 0;
    uTimeInfo.FrameID = 0;
    uTimeInfo.SubframeID = 0;
    uTimeInfo.SlotID = 0;
    uTimeInfo.SymbolID = 0;

    % Indicate compression method according to TS O-RAN.WG4.CUS
    switch oranConfig.method
        case 'BFP'
            udCompMeth = 1;
        case 'blockScaling'
            udCompMeth = 2;
        case 'muLaw'
            udCompMeth = 3;
        otherwise % No compression
            udCompMeth = 0;
    end

    % Get section configuration of first U-plane message
    uSectionsInfo.SectionID = 0; 
    uSectionsInfo.RB = 0;
    uSectionsInfo.SymInc = 0;
    uSectionsInfo.StartPRBu = 0;
    uSectionsInfo.NumPRBu = 0; % All PRBs in the specified SCS and carrier bandwidth
    uSectionsInfo.UdCompHdr = [cIQWidth(1),udCompMeth];
    uSectionsInfo.Reserved = 0;
    uSectionsInfo.PRBs = PRBsInfo(:,1); 
    
    % Set O-RAN configuration of first U-plane message
    uOran.TimeInfo = uTimeInfo;
    uOran.Sections = uSectionsInfo;

    % Generate eCPRI header fields
    ecpri.ProtocolRevision = 1;
    ecpri.Reserved = 0;
    ecpri.Concatenation = 0;
    ecpri.MessageType = 'IQData'; % Message type for U-plane message
    ecpri.eAxCID = [eCPRIConfig.DUPortID,eCPRIConfig.BandSectorID,eCPRIConfig.CCID, ...
        eCPRIConfig.RUPortID];
    sequenceID = 0;
    EBit = 1;
    subSequenceID = 0;
    ecpri.SEQ_ID = [sequenceID,EBit,subSequenceID];
    
    % Generate Ethernet header fields
    eth.DestinationAddress = ethernetConfig.destAddress;
    eth.SourceAddress = ethernetConfig.sourceAddress;
    etherType = 0xAEFE; % eCPRI uses Ethernet with the Ethertype 0xAEFE
    vlanTag = [ethernetConfig.TPID,ethernetConfig.priority,ethernetConfig.DEI, ...
        ethernetConfig.VID]; 
    eth.Type = [vlanTag etherType];
    
    % Combine all configurations (Ethernet header, eCPRI header, and O-RAN
    % information) of first U-plane message
    uMessagesConfig = struct;
    uMessagesConfig(1).eth = eth;
    uMessagesConfig(1).ecpri = ecpri;
    uMessagesConfig(1).oran = uOran;

    % Get configuration of remaining U-plane messages
    uPackets = size(PRBsInfo,2); % Number of U-plane messages per OFDM symbol
    symbolsPerSlot = waveConfig.symbolsPerSlot;
    slotsPerSubframe = waveConfig.slotsPerSubframe;
    for i = 1:uPackets-1
        uOran.TimeInfo.SubframeID = floor(i/(symbolsPerSlot*slotsPerSubframe));
        uOran.TimeInfo.SlotID = mod(floor(i/symbolsPerSlot),slotsPerSubframe);
        uOran.TimeInfo.SymbolID = mod(i,symbolsPerSlot);
        uOran.Sections.PRBs = PRBsInfo(:,i+1);
        ecpri.SEQ_ID(1,1) = ecpri.SEQ_ID(1,1)+1;
        ecpri.SEQ_ID(1,1) = mod(ecpri.SEQ_ID(1,1),256);
        uMessagesConfig(i+1).eth = eth;
        uMessagesConfig(i+1).ecpri = ecpri;
        uMessagesConfig(i+1).oran = uOran;
    end

    % Get time configuration of first C-plane message
    cTimeInfo.DataDirection = 1;
    cTimeInfo.PayloadVersion = 1;
    cTimeInfo.FilterIndex = 0;
    cTimeInfo.FrameID = 0;
    cTimeInfo.SubframeID = 0;
    cTimeInfo.SlotID = 0;
    cTimeInfo.SymbolID = 0; % Start symbol ID

    % Get section type and compression configurations of first C-plane message
    typeCompInfo.NumberOfSections = 1;
    typeCompInfo.SectionType = 1;
    typeCompInfo.UdCompHdr = 0; % This parameter will be ignored in DL
    typeCompInfo.Reserved = 0;
    
    % Generate section configuration of first C-plane message
    cSectionsInfo.SectionID = 0; 
    cSectionsInfo.RB = 0;
    cSectionsInfo.SymInc = 0;
    cSectionsInfo.StartPRBc = 0;
    cSectionsInfo.NumPRBc = 0; % All PRBs in the specified SCS and carrier bandwidth
    cSectionsInfo.REMAsk = ones(1,12);
    cSectionsInfo.NumSymbol = 14;
    cSectionsInfo.Ef = 0; % No section extensions
    cSectionsInfo.BeamID = 0; % No beamforming

    % Set O-RAN information of first C-plane message
    cOran.TimeInfo = cTimeInfo;
    cOran.SectionInfo = typeCompInfo;
    cOran.Sections = cSectionsInfo;

    % Combine all configurations (Ethernet header, eCPRI header, and O-RAN
    % information) of first C-plane message
    cMessagesConfig = struct;
    cMessagesConfig(1).eth = eth;
    ecpri.MessageType = 'RealTimeControlData'; % Message type for C-plane message
    ecpri.SEQ_ID(1,1) = 0;
    cMessagesConfig(1).ecpri = ecpri;
    cMessagesConfig(1).oran = cOran;

    % Get configuration of remaining C-plane messages
    NSubframes = 10;
    cPackets = slotsPerSubframe*NSubframes;
    for i = 1:cPackets-1
        cOran.TimeInfo.SubframeID = floor(i/slotsPerSubframe);
        cOran.TimeInfo.SlotID = mod(i,slotsPerSubframe);
        ecpri.SEQ_ID(1,1) = ecpri.SEQ_ID(1,1)+1;
        ecpri.SEQ_ID(1,1) = mod(ecpri.SEQ_ID(1,1),256);
        cMessagesConfig(i+1).eth = eth;
        cMessagesConfig(i+1).ecpri = ecpri;
        cMessagesConfig(i+1).oran = cOran;
    end
end

function generateCUMessages(cMessagesConfig,uMessagesConfig,mPlaneConfig,pcapFileName)
% Generate CU-plane messages and write the packets to PCAP file

    % Create a PCAP file writer object and write a global header to the PCAP file
    pcapObj = pcapWriter(FileName = pcapFileName);
    linkType = 1;  % Link type for Ethernet protocol 
    writeGlobalHeader(pcapObj, linkType);
    timeStamp = 0; % Packet arrival time in microseconds
    timeDelay = 40; % Time delay between packets in microseconds
    
    % Encode Ethernet header of CU-plane messages
    ethHeader = hORANProtocolEncoder.ethernetHeader(cMessagesConfig(1).eth);

    % Calculate number of U-plane packets per slot
    NuPacketsPerSlot = size(uMessagesConfig,2)/size(cMessagesConfig,2);
    
    % Generate CU-plane packets and write the packets to the PCAP file
    for i = 1:size(cMessagesConfig,2)
        % Encode eCPRI payload of C-plane message
        cECPRIPayload = hORANProtocolEncoder.eCPRIPayloadControl(cMessagesConfig(i).oran);
    
        % Encode eCPRI header of C-plane message
        cECPRIHeader = hORANProtocolEncoder.eCPRIHeader(cMessagesConfig(i).ecpri, ...
            length(cECPRIPayload),mPlaneConfig.eAxCIDBits);
    
        % Generate C-plane packet
        cPacket = [ethHeader; cECPRIHeader; cECPRIPayload];
    
        % Write C-plane packet to PCAP file
        write(pcapObj, cPacket, timeStamp);
        timeStamp = timeStamp+timeDelay*7;
     
        for j = 1:NuPacketsPerSlot
            % Encode eCPRI payload of U-plane message
            uECPRIPayload = hORANProtocolEncoder.eCPRIPayloadUser(uMessagesConfig(j+NuPacketsPerSlot*(i-1)).oran, ...
                mPlaneConfig.compMode,mPlaneConfig.byteOrder);
            
            % Encode eCPRI header of U-plane message
            uECPRIHeader = hORANProtocolEncoder.eCPRIHeader(uMessagesConfig(j+NuPacketsPerSlot*(i-1)).ecpri, ...
                length(uECPRIPayload),mPlaneConfig.eAxCIDBits);
            
            % Generate U-plane packet
            uPacket = [ethHeader; uECPRIHeader; uECPRIPayload];

            % Write U-plane packet to PCAP file
            write(pcapObj, uPacket, timeStamp);
            timeStamp = timeStamp+timeDelay;
        end
    end
end 

See Also

Functions