Main Content

Multi-User Transmit Beamforming with USRP™ Hardware

This example uses beamforming techniques to send two different payloads to two receivers simultaneously in the same frequency band. Channel estimates from the receivers are used continuously to update the transmitted beams.

The transmitter uses multiple USRP radios as a composite radio with 4 channels. Two X300/X310 radios or four N-series radios are required. All radios for the transmitter must be connected to the same PPS and 10 MHz clock generator via cables of equal lengths.

Besides the transmitter radios, the host computer must be connected to two more USRP radios, one for each receiver. The receiver radios must also be connected to the same 10 MHz clock generator.

To run this example, run MultiUserBeamformingExample, and then run helperMUBeamformRx1 and helperMUBeamformRx2 in two separate MATLAB sessions on the same host computer.

Configure radios

Find attached radios and allocate radios for transmitter and receivers.

radioConfig = helperMUBeamformAllocateRadios;
Checking radio connections...
% Save radio configuration to a file for helperMUBeamformRx1 and helperMUBeamformRx2
save(fullfile(tempdir,'helperMUBeamformRadioConfig.mat'),'radioConfig');

transmitter = comm.SDRuTransmitter('Platform',radioConfig.txPlatform,...
                                   'IPAddress',radioConfig.txIPAddrs);
transmitter.MasterClockRate = radioConfig.txMasterClockRate;
transmitter.InterpolationFactor = radioConfig.txInterpolationfactor;
transmitter.ChannelMapping = [1 2 3 4];
transmitter.CenterFrequency = 900e6;
transmitter.Gain = 8;
transmitter.ClockSource = 'External'; % Synchronize all 4 channels in frequency
transmitter.PPSSource = 'External';   % Synchronize all 4 channels in time

% Radio settings
transmitter
transmitter = 
  comm.SDRuTransmitter with properties:

                 Platform: 'X310'
                IPAddress: '192.168.70.2,192.168.60.2'
           ChannelMapping: [1 2 3 4]
          CenterFrequency: 900000000
    LocalOscillatorOffset: 0
                     Gain: 8
                PPSSource: 'External'
              ClockSource: 'External'
          MasterClockRate: 200000000
      InterpolationFactor: 500
        TransportDataType: 'int16'
          EnableBurstMode: false

Construct training signals and payloads

trainingSig = helperMUBeamformInitGoldSeq; % Based on Gold Sequences                    

% Construct payload 1:
% 64 symbols with IFFT length of 256
% Each symbol uses 8 subcarriers
% Subcarrier 5 uses 64-QAM. Each point of 64-QAM is used once.
% Other subcarriers use QPSK
modOut1 = [zeros(4,64);
           qammod(randperm(64)-1,64,'UnitAveragePower',true);   % 64-QAM
           qammod(randi([0 3],7,64),4,'UnitAveragePower',true); % QPSK
           zeros(256-4-8,64)];
payload1 = reshape(ifft(modOut1),[],1);
% Scale time-domain signal appropriately
payload1 = payload1/max(real(payload1))*0.5;

% Construct payload 2:
% 64 symbols with IFFT length of 256
% Each symbol uses 8 subcarriers
% Subcarrier 5 uses 16-QAM. Each point of 16-QAM is used 4 times.
% Other subcarriers use QPSK
modOut2 = [zeros(4,64);
           qammod([(randperm(16)-1) (randperm(16)-1) ...
                   (randperm(16)-1) (randperm(16)-1)], ...
                   16,'UnitAveragePower',true);                 % 16-QAM
           qammod(randi([0 3],7,64),4,'UnitAveragePower',true); % QPSK
           zeros(256-4-8,64)];
payload2 = reshape(ifft(modOut2),[],1);
% Scale time-domain signal appropriately
payload2 = payload2/max(real(payload2))*0.5;

Initialize variables and temporary files for channel feedback

% Have no knowledge of the channel yet
% Generate channel estimate randomly
lastFeedback1 = rand(1,4) + 1j*rand(1,4);
fid1 = fopen(fullfile(tempdir,'helperMUBeamformfeedback1.bin'),'wb');
fwrite(fid1,[real(lastFeedback1) imag(lastFeedback1)],'double');
fclose(fid1);

% Have no knowledge of the channel yet
% Generate channel estimate randomly
lastFeedback2 = rand(1,4) + 1j*rand(1,4);
fid2 = fopen(fullfile(tempdir,'helperMUBeamformfeedback2.bin'),'wb');
fwrite(fid2,[real(lastFeedback2) imag(lastFeedback2)],'double');
fclose(fid2);

% Open files for reading only
fid1 = fopen(fullfile(tempdir,'helperMUBeamformfeedback1.bin'),'rb');
fid2 = fopen(fullfile(tempdir,'helperMUBeamformfeedback2.bin'),'rb');

Main loop

disp('Sending two different payloads to two receivers simultaneously in the same frequency band ...');
Sending two different payloads to two receivers simultaneously in the same frequency band ...
disp('Channel estimates from the receivers are used continuously to update the transmitted beams.');
Channel estimates from the receivers are used continuously to update the transmitted beams.
disp('Please run helperMUBeamformRx1 and helperMUBeamformRx2 in two separate MATLAB sessions on this computer.');
Please run helperMUBeamformRx1 and helperMUBeamformRx2 in two separate MATLAB sessions on this computer.
for i = 1:30000
    fseek(fid1,0,'bof'); % Read from the beginning of the file
    % The channel between 4 TX antennas and 1 RX antenna is modeled
    % by 4 complex gains. This approximation works because the
    % signal has very narrow bandwidth (400k samples per second).
    channelEst1 = fread(fid1,8,'double');
    if length(channelEst1) == 8
        % File content has expected length
        channelEst1 = (channelEst1(1:4) + 1j*channelEst1(5:8)).';
        lastFeedback1 = channelEst1;
    else
        % Use last feedback
        channelEst1 = lastFeedback1;
    end

    fseek(fid2,0,'bof'); % Read from the beginning of the file
    % The channel between 4 TX antennas and 1 RX antenna is modeled
    % by 4 complex gains. This approximation works because the
    % signal has very narrow bandwidth (400k samples per second).
    channelEst2 = fread(fid2,8,'double');
    if length(channelEst2) == 8
        % File content has expected length
        channelEst2 = (channelEst2(1:4) + 1j*channelEst2(5:8)).';
        lastFeedback2 = channelEst2;
    else
        % Use last feedback
        channelEst2 = lastFeedback2;
    end

    % For payload 1, create a spectral null at receiver 2 by
    % inverting the channel response and applying phase offsets of
    % 0, pi/2, pi, and 3*pi/2 to achieve destructive interference.
    % Hence, payload 1 will be suppressed for receiver 2.
    beamWeight1 = 1./channelEst2;
    beamWeight1 = beamWeight1/max(abs(beamWeight1)); % Normalize the gain
    beamWeight1 = beamWeight1 .* [1 1j -1 -1j];

    % Rotate payload 1 so that receiver 1 does not need to
    % correct the phase of payload 1
    phaseCorrection1 = beamWeight1 * channelEst1.';
    phaseCorrection1 = phaseCorrection1/abs(phaseCorrection1);
    beamWeight1 = beamWeight1 ./ phaseCorrection1;

    % For payload 2, create a spectral null at receiver 1 by
    % inverting the channel response and applying phase offsets of
    % 0, pi/2, pi, and 3*pi/2 to achieve destructive interference
    % Hence, payload 2 will be suppressed for receiver 1.
    beamWeight2 = 1./channelEst1;
    beamWeight2 = beamWeight2/max(abs(beamWeight2));
    beamWeight2 = beamWeight2 .* [1 1j -1 -1j];

    % Rotate payload 2 so that receiver 2 does not need to
    % correct the phase of payload 2
    phaseCorrection2 = beamWeight2 * channelEst2.';
    phaseCorrection2 = phaseCorrection2/abs(phaseCorrection2);
    beamWeight2 = beamWeight2 ./ phaseCorrection2;

    % Beamforming for payload
    payload = payload1*beamWeight1 + payload2*beamWeight2;

    % Send signals to the radios
    txSig = [trainingSig; zeros(400,4); payload; zeros(100,4)] * 0.2;
    transmitter(txSig);
end

release(transmitter);

MultiUserBeamformingExample_01.png

MultiUserBeamformingExample_02.png

MultiUserBeamformingExample_03.png

MultiUserBeamformingExample_04.png

Appendix

This example uses the following helper functions: