Main Content

Phase Synchronize Operations on NI USRP Radios

Since R2025a

NI™ USRP™ radios have multiple local oscillators (LOs) on board that support independent tuning of transmit and receive channels. By default, each LO is generated independently from the reference clock. They are not guaranteed to be phase coherent or phase synchronized.

To achieve phase coherence between multiple channels on a single radio, follow these steps:

  • Use channels that share an LO, or, if your radio supports LO distribution, configure the channels to share an LO source.

  • If you are using LO distribution, configure this option on your radio by setting the synchronization options in the Configure Radio step in the Radio Setup wizard.

To achieve phase coherence between multiple radios, follow these steps:

  • Use a shared clock, time, and LO source. Make the physical connections to your radio using SMA cables.

  • Configure the LO options available on your radio by setting the synchronization options in the Configure Radio step in the Radio Setup wizard.

  • Time synchronize your radios. For details, see Time Synchronize Operations on NI USRP Radios.

USRP N320/N321 radios provide a method to distribute the LOs on a large number of channels. You can configure a USRP N321 radio to distribute and export a single LO signal using the synchronization options in the Radio Setup wizard. For information about how to connect your radio for LO distribution, see USRP N320/N321 LO Distribution.

To check if the LOs have stabilized at the desired frequency, use the loLockedStatus function.

Some applications, such as multiple-input multiple-output, beamforming, and direction of arrival, require the RF channels to be phase coherent. This means that the phase must be stable and consistent over time, but the phase does not need to be precisely aligned. Phase synchronization can be achieved by performing a phase calibration procedure and applying corrective phase shifts in software to align the phase of each channel.

Synchronize Phase on Multiple Radios

This example shows how to phase synchronize two NI USRP radios.

Introduction

In this example, you use two radios that share a clock, time, and LO source to achieve phase coherence.

You follow these steps:

  1. Configure one radio as a baseband transceiver and one as a baseband receiver.

  2. Use one radio to transmit a waveform, and repeatedly capture the waveform on two channels on each radio.

  3. Calculate the relative phase offset of the four channels from the multiple captures.

  4. Use the phase offset calculations to confirm that the four channels are phase coherent.

  5. Get an average of the calculated phase offsets that you can use correct for them in applications that require phase synchronization.

You can use the same process to measure the phase offsets between channels on a single radio.

Create Radio Objects

Create a radio object for each radio, specifying a radio setup configuration previously saved using the Radio Setup wizard.

radio1 = radioConfigurations("MyRadio");
radio2 = radioConfigurations("MyRadio2");

Select RF Parameters

Select RF parameters for configuring your baseband application objects and save them in a structure.

Select the highest supported sample rate for your radios.

bbtrx1Params.SampleRate = 250e6;
bbrx2Params.SampleRate = 250e6;

Select antennas to use with your radios.

bbtrx1Params.TransmitAntennas = "RF0:TX/RX";
bbtrx1Params.CaptureAntennas = ["RF0:RX2","RF1:RX2"];
bbrx2Params.Antennas = ["RF0:RX2","RF1:RX2"];

Set the transmit and capture datatypes to single.

bbtrx1Params.TransmitDataType = "single";
bbtrx1Params.CaptureDataType = "single";
bbrx2Params.CaptureDataType = "single";

Set the gain according to the local signal strength.

bbtrx1Params.TransmitRadioGain = 10;
bbtrx1Params.CaptureRadioGain = 10;
bbrx2Params.RadioGain = 10;

Set the center frequency for each antenna.

centerFrequency = 2.8e9;
bbtrx1Params.TransmitCenterFrequency = centerFrequency;
bbtrx1Params.CaptureCenterFrequency = [centerFrequency,centerFrequency];
bbrx2Params.CenterFrequency = [centerFrequency,centerFrequency];

Configure Radios using Baseband Application Objects

Create a basebandTransceiver for radio1 and a basebandReceiver for radio2 and configure the radios with the selected parameters. Set the Preload name-value argument to true to load the application onto the radio now.

params1 = [fieldnames(bbtrx1Params)'; struct2cell(bbtrx1Params)'];
bbtrx1 = basebandTransceiver(radio1,params1{:},Preload=true);
params2 = [fieldnames(bbrx2Params)'; struct2cell(bbrx2Params)'];
bbrx2 = basebandReceiver(radio2,params2{:},Preload=true);

Synchronize Radio Time

To synchronize both radios to use a common radio time, set the radio time on both radios to the same value on the next PPS signal. First, use the getTimeLastPPS function to get the time that the last PPS occurred.

tLastPPS = getTimeLastPPS(radio1);

When a PPS signal is detected, you have one second to set a new radio time. Check for a new signal every 0.1 seconds, then use the setTimeNextPPS function to set the radio time on both radios to zero.

while tLastPPS == getTimeLastPPS(radio1)
    pause(0.1)
end
setTimeNextPPS(radio1,0);
setTimeNextPPS(radio2,0);

To verify that the radio time is synchronized, verify that the last PPS signal occurred at the same time on both radios.

pause(1.1)
tLastPPS1 = getTimeLastPPS(radio1);
tLastPPS2 = getTimeLastPPS(radio2);
isequal(tLastPPS1,tLastPPS2)
ans = logical
   1

If the radio time synchronization is unsuccessful, try running this code section again.

Transmit a Sine Wave

Generate a 25 MHz sine wave for transmission.

sine = dsp.SineWave;
sine.OutputDataType = "single";
sine.Frequency = 25e6;
sine.ComplexOutput = true;
sine.SamplesPerFrame = 2*bbtrx1.SampleRate/sine.Frequency;
sine.SampleRate = bbtrx1.SampleRate;
transmitData = sine();

Transmit the tone continuously using the transmit function.

transmit(bbtrx1,transmitData,'continuous');

Capture Data and Calculate Phase Offsets

Set a capture length of 50 ms, a start time offset of 1 s for each capture.

captureLength = milliseconds(50);
startTimeOffset = 1;

Set the number of captures to 20.

numCaptures = 20;

Create a vector for storing the offset results.

offsets = zeros(numCaptures,4);

Check that the LOs have locked to the configured settings by using the loLockedStatus function. If they are not locked, wait for a lock.

while ~loLockedStatus(radio1) || ~loLockedStatus(radio2)
    disp("LO not locked")
    pause(1)
end

Capture data repeatedly in a loop.

for n = 1:numCaptures

Set the start time relative to the current radio time.

    startTime = getRadioTime(radio1) + startTimeOffset;

Capture data in the background on both radios by using the capture function.

    capture(bbtrx1,captureLength,Background=true,StartTime=startTime);
    capture(bbrx2,captureLength,Background=true,StartTime=startTime);

Check that the capture function call did not take longer than expected. If an error occurs, try increasing the start time offset.

    if getRadioTime(radio1) > startTime
        stopCapture(bbtrx1);
        stopCapture(bbrx2);
        error("capture call took longer than scheduled capture time");
    end

Wait for the capture to complete.

    while isCapturing(bbtrx1)
        pause(1);
    end

Retrieve the captured data.

    data1=captureOutputs(bbtrx1);
    data2=captureOutputs(bbrx2);
    data = [data1, data2];
    [r,c] = size(data);
    fftData = fft(data);

Calculate the phase offsets using the captured data.

    for m = 1:c
        offsets(m,m) = rad2deg(angle(max(fftData(:,m))/max(fftData(:,1))));
    end
end

When all captures have completed, stop the transmission.

stopTransmission(bbtrx1);

Calculate the standard deviation of the phase offsets.

stdev = std(offsets);

Phase Synchronize Captures by Correcting for Offsets

View the phase offsets you calculated from your captured data.

offsets
offsets = 20×4

         0   -5.3575  -61.3953  -85.0506
         0   -5.4357  -61.4488  -85.3063
         0   -5.2094  -61.2887  -85.2197
         0   -5.3450  -61.2465  -85.1117
         0   -5.4417  -61.3940  -85.0393
         0   -5.4427  -61.2803  -85.1316
         0   -5.4463  -61.3905  -85.2179
         0   -5.3290  -61.2600  -84.9629
         0   -5.4590  -61.4355  -85.1771
         0   -5.6650  -61.3777  -85.2601
         0   -5.4284  -61.2998  -85.0758
         0   -5.3314  -61.3507  -85.1362
         0   -5.5819  -61.3163  -84.9161
         0   -5.3999  -61.1081  -85.0714
         0   -5.5169  -61.3509  -85.1137
      ⋮

stdev
stdev = 1×4

         0   35.4310   33.3283   32.1353

If you are satisfied with the phase coherence for your application, calculate the average phase offsets.

mean(offsets)
ans = 1×4

         0   -5.4565  -61.3463  -85.1425

You can use these results to apply phase corrections in applications that require phase synchronization.

See Also

Tools

Functions

Objects

Topics