aboutsummaryrefslogtreecommitdiffstats
path: root/host/examples/sync_to_gps.cpp
blob: 3f19d9b13dac976d550be8c3975922681243a19b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//
// Copyright 2016 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//

#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/utils/thread.hpp>
#include <boost/format.hpp>
#include <boost/program_options.hpp>
#include <chrono>
#include <iostream>
#include <thread>

namespace po = boost::program_options;

void print_notes(void)
{
    // Helpful notes
    std::cout << boost::format(
        "**************************************Helpful Notes on Clock/PPS "
        "Selection**************************************\n");
    std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS "
                               "signals are now from the GPSDO.\n");
    std::cout << boost::format(
        "If you would like to use the internal reference(TCXO) in other applications, "
        "you must configure that explicitly.\n");
    std::cout << boost::format(
        "You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n");
    std::cout << boost::format(
        "********************************************************************************"
        "********************************\n");
}

int UHD_SAFE_MAIN(int argc, char* argv[])
{
    std::string args;

    // Set up program options
    po::options_description desc("Allowed options");
    // clang-format off
    desc.add_options()
    ("help", "help message")
    ("args", po::value<std::string>(&args)->default_value(""), "USRP device arguments")
    ;
    // clang-format on
    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    // Print the help message
    if (vm.count("help")) {
        std::cout << boost::format("Synchronize USRP to GPS %s") % desc << std::endl;
        return EXIT_FAILURE;
    }

    // Create a USRP device
    std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args;
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
    std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string();

    try {
        size_t num_mboards    = usrp->get_num_mboards();
        size_t num_gps_locked = 0;
        for (size_t mboard = 0; mboard < num_mboards; mboard++) {
            std::cout << "Synchronizing mboard " << mboard << ": "
                      << usrp->get_mboard_name(mboard) << std::endl;

            // Set references to GPSDO
            usrp->set_clock_source("gpsdo", mboard);
            usrp->set_time_source("gpsdo", mboard);

            std::cout << std::endl;
            print_notes();
            std::cout << std::endl;

            // Check for 10 MHz lock
            std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(mboard);
            if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked")
                != sensor_names.end()) {
                std::cout << "Waiting for reference lock..." << std::flush;
                bool ref_locked = false;
                for (int i = 0; i < 30 and not ref_locked; i++) {
                    ref_locked = usrp->get_mboard_sensor("ref_locked", mboard).to_bool();
                    if (not ref_locked) {
                        std::cout << "." << std::flush;
                        std::this_thread::sleep_for(std::chrono::seconds(1));
                    }
                }
                if (ref_locked) {
                    std::cout << "LOCKED" << std::endl;
                } else {
                    std::cout << "FAILED" << std::endl;
                    std::cout << "Failed to lock to GPSDO 10 MHz Reference. Exiting."
                              << std::endl;
                    exit(EXIT_FAILURE);
                }
            } else {
                std::cout << boost::format(
                    "ref_locked sensor not present on this board.\n");
            }

            // Wait for GPS lock
            bool gps_locked = usrp->get_mboard_sensor("gps_locked", mboard).to_bool();
            if (gps_locked) {
                num_gps_locked++;
                std::cout << boost::format("GPS Locked\n");
            } else {
                std::cerr
                    << "WARNING:  GPS not locked - time will not be accurate until locked"
                    << std::endl;
            }

            // Set to GPS time
            uhd::time_spec_t gps_time = uhd::time_spec_t(
                int64_t(usrp->get_mboard_sensor("gps_time", mboard).to_int()));
            usrp->set_time_next_pps(gps_time + 1.0, mboard);

            // Wait for it to apply
            // The wait is 2 seconds because N-Series has a known issue where
            // the time at the last PPS does not properly update at the PPS edge
            // when the time is actually set.
            std::this_thread::sleep_for(std::chrono::seconds(2));

            // Check times
            gps_time = uhd::time_spec_t(
                int64_t(usrp->get_mboard_sensor("gps_time", mboard).to_int()));
            uhd::time_spec_t time_last_pps = usrp->get_time_last_pps(mboard);
            std::cout << "USRP time: "
                      << (boost::format("%0.9f") % time_last_pps.get_real_secs())
                      << std::endl;
            std::cout << "GPSDO time: "
                      << (boost::format("%0.9f") % gps_time.get_real_secs()) << std::endl;
            if (gps_time.get_real_secs() == time_last_pps.get_real_secs())
                std::cout << std::endl
                          << "SUCCESS: USRP time synchronized to GPS time" << std::endl
                          << std::endl;
            else
                std::cerr << std::endl
                          << "ERROR: Failed to synchronize USRP time to GPS time"
                          << std::endl
                          << std::endl;
        }

        if (num_gps_locked == num_mboards and num_mboards > 1) {
            // Check to see if all USRP times are aligned
            // First, wait for PPS.
            uhd::time_spec_t time_last_pps = usrp->get_time_last_pps();
            while (time_last_pps == usrp->get_time_last_pps()) {
                std::this_thread::sleep_for(std::chrono::milliseconds(1));
            }

            // Sleep a little to make sure all devices have seen a PPS edge
            std::this_thread::sleep_for(std::chrono::milliseconds(200));

            // Compare times across all mboards
            bool all_matched              = true;
            uhd::time_spec_t mboard0_time = usrp->get_time_last_pps(0);
            for (size_t mboard = 1; mboard < num_mboards; mboard++) {
                uhd::time_spec_t mboard_time = usrp->get_time_last_pps(mboard);
                if (mboard_time != mboard0_time) {
                    all_matched = false;
                    std::cerr << (boost::format("ERROR: Times are not aligned: USRP "
                                                "0=%0.9f, USRP %d=%0.9f")
                                     % mboard0_time.get_real_secs() % mboard
                                     % mboard_time.get_real_secs())
                              << std::endl;
                }
            }
            if (all_matched) {
                std::cout << "SUCCESS: USRP times aligned" << std::endl << std::endl;
            } else {
                std::cout << "ERROR: USRP times are not aligned" << std::endl
                          << std::endl;
            }
        }
    } catch (std::exception& e) {
        std::cout << boost::format("\nError: %s") % e.what();
        std::cout << boost::format(
            "This could mean that you have not installed the GPSDO correctly.\n\n");
        std::cout << boost::format("Visit one of these pages if the problem persists:\n");
        std::cout << boost::format(
            " * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html");
        std::cout << boost::format(
            " * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n");
        std::cout << boost::format(
            " * E3X0: http://files.ettus.com/manual/page_usrp_e3x0.html#e3x0_hw_gps\n\n");
        exit(EXIT_FAILURE);
    }

    return EXIT_SUCCESS;
}