mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-15 21:01:26 +00:00
202 lines
8.5 KiB
Text
202 lines
8.5 KiB
Text
/*! \page page_sync Device Synchronization
|
|
|
|
\tableofcontents
|
|
|
|
The following application notes explain how to synchronize multiple USRP
|
|
devices with the goal of transmitting or receiving time-aligned samples
|
|
for MIMO or other applications requiring multiple USRP devices operating
|
|
synchronously.
|
|
|
|
<b>Note:</b> The following synchronization notes do not apply to USRP1,
|
|
which does not support the advanced features available in newer
|
|
products.
|
|
|
|
\section sync_commonref Common Reference Signals
|
|
|
|
USRP devices take two reference signals in order to synchronize clocks
|
|
and time:
|
|
|
|
- A 10 MHz reference to provide a single frequency reference for both
|
|
devices.
|
|
- A pulse-per-second (PPS) to synchronize the sample time across
|
|
devices.
|
|
- A MIMO cable transmits an encoded time message from one device to
|
|
another.
|
|
|
|
\subsection sync_commonref_pps PPS and 10 MHz reference signals
|
|
|
|
Connect the front panel SMA connectors to the reference sources.
|
|
Typically, these signals are provided by an external GPSDO. However,
|
|
some USRP models can provide these signals from an optional internal
|
|
GPSDO.
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
usrp->set_clock_source("external");
|
|
usrp->set_time_source("external");
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
<b>Note:</b> Sometimes the delay on the PPS signal will cause it to arrive
|
|
inside the timing margin the FPGA sampling clock, causing PPS edges to
|
|
be separated by less or more than 100 million cycles of the FPGA clock.
|
|
If this is the case, you can change the edge reference of the PPS signal
|
|
with this parameter:
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
usrp->set_time_source("_external_");
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
<b>Note2:</b> For users generating their own signals for the external SMA
|
|
connectors, the PPS should be clocked from the 10 MHz reference. See the
|
|
application notes for your device for specific signal requirements.
|
|
|
|
\subsection sync_commonref_mimo MIMO cable reference signals
|
|
|
|
Use the MIMO expansion cable to share reference sources (USRP2 and
|
|
N-Series). The MIMO cable can be used synchronize one device to another
|
|
device. Users of the MIMO cable may use Method 1 (explained below) to
|
|
synchronize multiple pairs of devices.
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
usrp->set_clock_source("mimo");
|
|
usrp->set_time_source("mimo");
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
\section sync_time Synchronizing the Device Time
|
|
|
|
The purpose of the PPS signal is to synchronously latch a time into the
|
|
device. You can use the uhd::usrp::multi_usrp::set_time_next_pps() function to either
|
|
initialize the sample time to 0 or an absolute time, such as GPS time or
|
|
UTC time. For the purposes of synchronizing devices, it doesn't matter
|
|
what time you initialize to when using uhd::usrp::multi_usrp::set_time_next_pps().
|
|
|
|
\subsection sync_time_reg Method 1 - poll the USRP time registers
|
|
|
|
One way to initialize the PPS edge is to poll the "last PPS" time from
|
|
the USRP device. When the last PPS time increments, the user can
|
|
determine that a PPS has occurred:
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
const uhd::time_spec_t last_pps_time = usrp->get_time_last_pps();
|
|
while (last_pps_time == usrp->get_time_last_pps()){
|
|
//sleep 100 milliseconds (give or take)
|
|
}
|
|
// This command will be processed fairly soon after the last PPS edge:
|
|
usrp->set_time_next_pps(uhd::time_spec_t(0.0));
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
\subsection sync_time_gpsdo Method 2 - query the GPSDO for seconds
|
|
|
|
Most GPSDOs can be configured to output a NMEA string over the serial
|
|
port once every PPS. The user can wait for this string to determine the
|
|
PPS edge, and the user can also parse this string to determine GPS time:
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
//call user's function to wait for NMEA message...
|
|
usrp->set_time_next_pps(uhd::time_spec_t(0.0));
|
|
|
|
-- OR --
|
|
|
|
//call user's function to wait for NMEA message...
|
|
//call user's function to parse the NMEA message...
|
|
gps_time = VALUE_IN_NMEA_MESSAGE;
|
|
// At the next PPS edge, set the device time to the GPS time:
|
|
usrp->set_time_next_pps(uhd::time_spec_t(gps_time+1));
|
|
// Now the device time should be in sync with the GPS time.
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
Take a look at the `sync_to_gps` example for more detail.
|
|
|
|
\subsection sync_time_mimocable Method 3 - MIMO cable
|
|
|
|
Note: This only applies to USRP2 and N200/N210. This method does *not*
|
|
require a separate PPS input to the devices, but it is limited to
|
|
a total of 2 USRPs.
|
|
|
|
A USRP2 device can synchronize its time to another USRP device via the
|
|
MIMO cable. Unlike the other methods, this does not use a real "pulse
|
|
per second". Rather, the USRP device sends an encoded time message over
|
|
the MIMO cable. The slave device will automatically synchronize to the
|
|
time on the master device. See \ref usrp2_mimocable for more detail.
|
|
|
|
\section sync_phase Synchronizing Channel Phase
|
|
|
|
\subsection sync_phase_cordics Align CORDICs in the DSP
|
|
|
|
In order to achieve phase alignment between USRP devices, the CORDICS in
|
|
both devices must be aligned with respect to each other. This is easily
|
|
achieved by issuing stream commands with a time spec property, which
|
|
instructs the streaming to begin at a specified time. Since the devices
|
|
are already synchronized via the 10 MHz and PPS inputs, the streaming
|
|
will start at exactly the same time on both devices. The CORDICs are
|
|
reset at each start-of-burst command, so users should ensure that every
|
|
start-of-burst also has a time spec set.
|
|
|
|
For receive, a burst is started when the user issues a stream command.
|
|
This stream command should have a time spec set:
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
|
|
stream_cmd.num_samps = samps_to_recv;
|
|
stream_cmd.stream_now = false;
|
|
stream_cmd.time_spec = time_to_recv;
|
|
usrp->issue_stream_cmd(stream_cmd);
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
For transmit, a burst is started when the user calls send(). The
|
|
metadata should have a time spec set: :
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
uhd::tx_metadata_t md;
|
|
md.start_of_burst = true;
|
|
md.end_of_burst = false;
|
|
md.has_time_spec = true;
|
|
md.time_spec = time_to_send;
|
|
|
|
//send a single packet
|
|
size_t num_tx_samps = tx_streamer->send(buffs, samps_to_send, md);
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
\subsection sync_phase_lo Align LOs in the front-end (SBX, UBX)
|
|
|
|
Using timed commands, multiple frontends can be tuned at a specific
|
|
time. This timed-tuning ensures that the phase offsets between VCO/PLL
|
|
chains will remain constant after each re-tune. See notes below:
|
|
|
|
- Phase synchronization with the UBX is only supported on the X3x0 Series
|
|
- Phase synchronization with the SBX works on both N2x0 and X3x0 Series
|
|
- There is a random phase offset between any two frontends
|
|
- This phase offset is different for different LO frequencies
|
|
- This phase offset remains constant after retuning
|
|
- This phase offset will drift over time due to thermal and other characteristics
|
|
- Periodic calibration will be necessary for phase-coherent applications
|
|
|
|
Code snippet example, tuning with timed commands:
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
|
|
//we will tune the frontends in 100ms from now
|
|
uhd::time_spec_t cmd_time = usrp->get_time_now() + uhd::time_spec_t(0.1);
|
|
|
|
//sets command time on all devices
|
|
//the next commands are all timed
|
|
usrp->set_command_time(cmd_time);
|
|
|
|
//tune channel 0 and channel 1
|
|
usrp->set_rx_freq(1.03e9, 0); // Channel 0
|
|
usrp->set_rx_freq(1.03e9, 1); // Channel 1
|
|
|
|
//end timed commands
|
|
usrp->clear_command_time();
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
\subsection sync_phase_lootherfe Align LOs in the front-end (others)
|
|
|
|
After tuning the RF front-ends, each local oscillator may have a random
|
|
phase offset due to the dividers in the VCO/PLL chains. This offset will
|
|
remain constant after the device has been initialized, and will remain
|
|
constant until the device is closed or re-tuned. This phase offset is
|
|
typically removed by the user in MIMO applications, using a training
|
|
sequence to estimate the offset. It will be necessary to re-align the
|
|
LOs after each tune command.
|
|
|
|
*/
|
|
// vim:ft=doxygen:
|