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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
//
// Copyright 2012,2014-2016 Ettus Research LLC
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: GPL-3.0-or-later
//
#include <uhd/usrp/multi_usrp.hpp>
#include <uhd/usrp_clock/multi_usrp_clock.hpp>
#include <uhd/utils/algorithm.hpp>
#include <uhd/utils/paths.hpp>
#include <uhd/utils/safe_main.hpp>
#include <uhd/utils/thread.hpp>
#include <boost/filesystem.hpp>
#include <boost/format.hpp>
#include <boost/program_options.hpp>
#include <chrono>
#include <cmath>
#include <complex>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <string>
#include <thread>
namespace po = boost::program_options;
namespace fs = boost::filesystem;
const size_t WARMUP_TIMEOUT_MS = 30000;
const size_t LOCK_TIMEOUT_MS = 60000;
void print_notes(void)
{
// Helpful notes
std::cout << "**************************************Helpful Notes on Clock/PPS "
"Selection**************************************\n";
std::cout << "As you can see, the default 10 MHz Reference and 1 PPS "
"signals are now from the GPSDO.\n";
std::cout
<< "If you would like to use the internal reference(TCXO) in other applications, "
"you must configure that explicitly.\n";
std::cout << "***********************************************************************"
"*********"
"********************************\n";
}
int query_clock_sensors(const std::string& args)
{
std::cout << boost::format("\nCreating the clock device with: %s...\n") % args;
uhd::usrp_clock::multi_usrp_clock::sptr clock =
uhd::usrp_clock::multi_usrp_clock::make(args);
// Verify GPS sensors are present
std::vector<std::string> sensor_names = clock->get_sensor_names(0);
if (std::find(sensor_names.begin(), sensor_names.end(), "gps_locked")
== sensor_names.end()) {
std::cout << "\ngps_locked sensor not found. This could mean that "
"this unit does not have a GPSDO.\n\n";
return EXIT_FAILURE;
}
// Print NMEA strings
try {
uhd::sensor_value_t gpgga_string = clock->get_sensor("gps_gpgga");
uhd::sensor_value_t gprmc_string = clock->get_sensor("gps_gprmc");
uhd::sensor_value_t servo_string = clock->get_sensor("gps_servo");
std::cout << boost::format("\nPrinting available NMEA strings:\n");
std::cout << boost::format("%s\n%s\n") % gpgga_string.to_pp_string()
% gprmc_string.to_pp_string();
std::cout << boost::format("\nPrinting GPS servo status:\n");
std::cout << boost::format("%s\n\n") % servo_string.to_pp_string();
} catch (const uhd::lookup_error&) {
std::cout << "GPxxx NMEA strings not implemented for this device." << std::endl;
}
try {
uhd::sensor_value_t gngga_string = clock->get_sensor("gps_gngga");
uhd::sensor_value_t gnrmc_string = clock->get_sensor("gps_gnrmc");
uhd::sensor_value_t timelock_string = clock->get_sensor("gps_timelock");
uhd::sensor_value_t discsrc_string = clock->get_sensor("gps_discsrc");
std::cout << boost::format("%s\n%s\n") % gngga_string.to_pp_string()
% gnrmc_string.to_pp_string();
std::cout << boost::format("\nPrinting GPS timelock:\n");
std::cout << boost::format("%s\n\n") % timelock_string.to_pp_string();
std::cout << boost::format("%s\n\n") % discsrc_string.to_pp_string();
} catch (const uhd::lookup_error&) {
std::cout << "GNxxx NMEA strings or timelock not implemented for this device." << std::endl;
}
std::cout << boost::format("GPS Epoch time: %.5f seconds\n")
% clock->get_sensor("gps_time").to_real();
std::cout << boost::format("PC Clock time: %.5f seconds\n") % time(NULL);
// finished
std::cout << "\nDone!\n\n";
return EXIT_SUCCESS;
}
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(""), "Device address arguments specifying a single USRP")
("clock", "query a clock device's sensors")
;
// 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 << "Query GPSDO Sensors, try to lock the reference oscillator to "
"the GPS disciplined clock, and set the device time to GPS time"
<< std::endl
<< std::endl
<< desc;
return EXIT_FAILURE;
}
// If specified, query a clock device instead
if (vm.count("clock")) {
return query_clock_sensors(args);
}
// 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();
// Verify GPS sensors are present (i.e. EEPROM has been burnt)
std::vector<std::string> sensor_names = usrp->get_mboard_sensor_names(0);
std::cout << " Sensors: " << std::endl;
for (const auto& name : sensor_names) {
std::cout << " Sensor: " << name << std::endl;
}
if (std::find(sensor_names.begin(), sensor_names.end(), "gps_locked")
== sensor_names.end()) {
std::cout << "\ngps_locked sensor not found. This could mean that "
"you have not installed the GPSDO correctly.\n\n";
std::cout << "Visit one of these pages if the problem persists:\n";
std::cout << " * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html\n";
std::cout << " * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n";
return EXIT_FAILURE;
}
bool ref_set_to_gpsdo = false;
// Set clock source to gpsdo if supported
if (uhd::has(usrp->get_clock_sources(0), "gpsdo")) {
std::cout << "Setting the reference clock source to \"gpsdo\"..." << std::endl;
usrp->set_clock_source("gpsdo");
ref_set_to_gpsdo = true;
}
std::cout << "Clock source is now " << usrp->get_clock_source(0) << std::endl;
// Set time source to gpsdo if supported
if (uhd::has(usrp->get_time_sources(0), "gpsdo")) {
std::cout << "Setting the reference clock source to \"gpsdo\"..." << std::endl;
usrp->set_time_source("gpsdo");
ref_set_to_gpsdo = true;
}
std::cout << "Time source is now " << usrp->get_time_source(0) << std::endl;
if (not ref_set_to_gpsdo) {
std::cerr << "ERROR: Unable to set clock or time reference to \"gpsdo\""
<< std::endl;
return EXIT_FAILURE;
}
// Check for ref lock
if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked")
!= sensor_names.end()) {
std::cout << "Waiting for ref_locked..." << std::flush;
uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked", 0);
auto end = std::chrono::steady_clock::now() + std::chrono::seconds(30);
while (!ref_locked.to_bool() && std::chrono::steady_clock::now() < end) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
ref_locked = usrp->get_mboard_sensor("ref_locked", 0);
std::cout << "." << std::flush;
}
if (not ref_locked.to_bool()) {
std::cout << "USRP NOT Locked to Reference.\n";
std::cout << "Double check installation instructions (N2X0/E1X0 only): "
"https://www.ettus.com/content/files/gpsdo-kit_4.pdf\n\n";
return EXIT_FAILURE;
} else {
std::cout << "USRP Locked to Reference.\n";
}
} else {
std::cout << "ref_locked sensor not present on this board.\n";
}
print_notes();
// The TCXO has a long warm up time, so wait up to 30 seconds for sensor data
// to show up
std::cout << "Waiting for the GPSDO to warm up..." << std::flush;
auto end = std::chrono::steady_clock::now() + std::chrono::seconds(30);
while (std::chrono::steady_clock::now() < end) {
try {
usrp->get_mboard_sensor("gps_locked", 0);
break;
} catch (std::exception&) {
}
std::this_thread::sleep_for(std::chrono::milliseconds(250));
std::cout << "." << std::flush;
}
std::cout << std::endl;
try {
usrp->get_mboard_sensor("gps_locked", 0);
} catch (std::exception&) {
std::cout << "No response from GPSDO in 30 seconds" << std::endl;
return EXIT_FAILURE;
}
std::cout << "The GPSDO is warmed up and talking." << std::endl;
// Check for GPS lock
uhd::sensor_value_t gps_locked = usrp->get_mboard_sensor("gps_locked", 0);
;
if (not gps_locked.to_bool()) {
std::cout << "\nGPS does not have lock. Wait a few minutes and try again.\n";
std::cout << "NMEA strings and device time may not be accurate "
"until lock is achieved.\n\n";
} else {
std::cout << "GPS Locked";
}
// Check PPS and compare UHD device time to GPS time
uhd::sensor_value_t gps_time = usrp->get_mboard_sensor("gps_time");
uhd::time_spec_t last_pps_time = usrp->get_time_last_pps();
// we only care about the full seconds
signed gps_seconds = gps_time.to_int();
long long pps_seconds = last_pps_time.to_ticks(1.0);
if (pps_seconds != gps_seconds) {
std::cout << "\nTrying to align the device time to GPS time..." << std::endl;
gps_time = usrp->get_mboard_sensor("gps_time");
// set the device time to the GPS time
// getting the GPS time returns just after the PPS edge, so just add a
// second and set the device time at the next PPS edge
usrp->set_time_next_pps(uhd::time_spec_t(gps_time.to_int() + 1.0));
// allow some time to make sure the PPS has come…
std::this_thread::sleep_for(std::chrono::milliseconds(1100));
//…then ask
gps_seconds = usrp->get_mboard_sensor("gps_time").to_int();
pps_seconds = usrp->get_time_last_pps().to_ticks(1.0);
}
if (pps_seconds == gps_seconds) {
std::cout << "GPS and UHD Device time are aligned.\n";
} else {
std::cout << "Could not align UHD Device time to GPS time. Giving up.\n";
}
std::cout << boost::format("last_pps: %ld vs gps: %ld.") % pps_seconds % gps_seconds
<< std::endl;
// print NMEA strings
std::cout << "Printing available NMEA strings:\n";
try {
std::cout << usrp->get_mboard_sensor("gps_gpgga").to_pp_string() << std::endl;
} catch (uhd::value_error&) {
std::cout << "GPGGA string not available for this device." << std::endl;
}
try {
std::cout << usrp->get_mboard_sensor("gps_gprmc").to_pp_string() << std::endl;
} catch (uhd::value_error&) {
std::cout << "GPRMC string not available for this device." << std::endl;
}
try {
std::cout << usrp->get_mboard_sensor("gps_gngga").to_pp_string() << std::endl;
} catch (uhd::value_error&) {
std::cout << "GNGGA string not available for this device." << std::endl;
}
try {
std::cout << usrp->get_mboard_sensor("gps_gnrmc").to_pp_string() << std::endl;
} catch (uhd::value_error&) {
std::cout << "GNRMC string not available for this device." << std::endl;
}
std::cout << boost::format("GPS Epoch time at last PPS: %.5f seconds\n")
% usrp->get_mboard_sensor("gps_time").to_real();
std::cout << boost::format("UHD Device time last PPS: %.5f seconds\n")
% (usrp->get_time_last_pps().get_real_secs());
std::cout << boost::format("UHD Device time right now: %.5f seconds\n")
% (usrp->get_time_now().get_real_secs());
std::cout << boost::format("PC Clock time: %.5f seconds\n") % time(NULL);
// finished
std::cout << "\nDone!\n\n";
return EXIT_SUCCESS;
}
|