/*! \page page_gpsdo_x3x0 Internal GPSDO Application Notes (USRP-X3x0 Models)

\tableofcontents

This application note describes the use of the board-mounted GPS
Disciplined OCXO, as used with the USRP X300/X310. For information
regarding the GPSDO that is compatible with the USRP N2xx or E1xx,
please see \ref page_gpsdo.

\section gpsdox_specs Specifications

-   **Receiver type**: 50 channel with WAAS, EGNOS, MSAS
-   **10 MHz ADEV**: 5e-11 over \>24h
-   **1PPS RMS jitter**: \<50ns 1-sigma
-   **Holdover**: \<20us over 3h

<b>Phase noise</b>:

 Offset  |   OCXO Phase noise power
---------|----------------------------
1Hz      |-75dBc/Hz
10Hz     |-110dBc/Hz
100Hz    |-132dBc/Hz
1kHz     |-142dBc/Hz
10kHz    |-145dBc/Hz
100kHz   |-150dBc/Hz

<b>Antenna Types:</b>

The GPSDO is capable of supplying a 3V for active GPS antennas or
supporting passive antennas.

\section gpsdox_install Installation Instructions

To install the GPSDO, you must insert it into the slot on the board near
the 10 MHz Reference SMA. Keep in mind that the two sides of the GPSDO
have a different number of pins. When inserting the GPSDO, make sure to
press down firmly and evenly. When turning on the USRP X3x0 device, a
green LED should illuminate on the GPSDO. This signifies that the unit
has successfully been placed.

<b>NOTE: The pins on the GPSDO are very fragile. Be sure to press down
evenly, or the pins may bend or break. Once the GPSDO is in place, we
very highly discourage further removal, as this also risks damaging the
pins.</b>

\section gpsdox_using Using the GPSDO in Your Application

If a GPSDO is detected at startup, the USRP will have `gpsdo` clock and time
source options.

Select these by using UHD's clock and time source APIs:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    usrp->set_clock_source("gpsdo");
    usrp->set_time_source("gpsdo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

That will lock the reference clock to the disciplined 10MHz oscillator, and
will make the USRP listen for time signal pulses coming from the GPSDO.

However, you will still need to explicitly set the device time on such a PPS
edge, using the `set_time_next_pps()` or `set_time_unknown_pps()`
calls.

GPS data is obtained through the **mboard_sensors** interface. To
retrieve the current GPS time, use the **gps_time** sensor:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    usrp->get_mboard_sensor("gps_time");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The returned value will be the current epoch time, in seconds since
January 1, 1970. This value is readily converted into human-readable
format using the **time.h** library in C, **std::chrono** in C++,
etc.

\subsection gpsdox_setting_gps_time Setting Device Time to GPS time

If you want to set the device time to GPS time, the following procedure makes
sure you don't encounter timing problems while doing so:

1. Wait for GPS lock, and then set the time source to GPSDO,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    while(! (usrp->get_mboard_sensor("gps_locked",0).to_bool()) ) {
        std::this_thread::sleep(std::chrono::seconds(2));
    }
    usrp->set_time_source("gpsdo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2. poll on `usrp->get_time_last_pps()` until a change is seen, and then sleep 200ms to
allow new time data coming from the GPSDO to propagate through the system,
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    uhd::time_spec_t last = usrp->get_time_last_pps();
    uhd::time_spec_t next = usrp->get_time_last_pps();
    while(next == last) {
        std::this_thread::sleep(std::chrono::milliseconds(50));
        last = next;
        next = usrp->get_time_last_pps();
    }
    std::this_thread::sleep(std::chrono::milliseconds(200));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3.  use
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
usrp->set_time_next_pps(uhd::time_spec_t(usrp->get_mboard_sensor("gps_time").to_int()+1));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
to set the time,
4.  poll on `usrp->get_time_last_pps()` until a change is seen and sleep 200ms (allow NMEA
string to propagate),
5.  Verify that `usrp->get_time_last_pps()` and `usrp->get_mboard_sensor("gps_time")`
return the same time.

\subsection gpsdox_sensors Further GPS sensors

Other information can be fetched as well. You can query the lock status
with the **gps_locked** sensor, as well as obtain raw NMEA sentences
using the **gps_gprmc**, and **gps_gpgga** sensors. Location
information can be parsed out of the **gps_gpgga** sensor by using **gpsd**
or another NMEA parser.

\subsection gpsdox_custom_cmds Sending custom commands to the GPS module

\b WARNING: This is considered advanced usage, and sending custom commands to
the GPS module can cause the GPSDO to go into an undefined state, or stop
functioning correctly. Only use this if you are sure of what you are doing.

The GPS module inside the X3x0 supports many more features than are exposed by
the regular UHD APIs. To access the full feature set of the GPS module, you
need to send custom commands to the GPS module. This can be done by accessing
the **gps_iface** feature, which can be done as follows:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
    if (usrp->get_mb_controller(0)->has_feature<uhd::features::gps_iface>()) {
        auto& gps_iface =
            usrp->get_mb_controller(0)->get_feature<uhd::features::gps_iface>();
        std::string response = gps_iface->send_cmd("SYNC:FEE?");
	std::cout << "Frequency Error Estimate: " << response << std::endl;
    }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

For a full listing of the available commands, consult the Jackson Labs LC_XO
manual.


*/
// vim:ft=doxygen:
